数据结构【线性表篇】(二)
文章目录
前言
为什么突然想学算法了?
> 用较为"官方"的语言讲 ,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。
> 但从实际而言 ,是因为当下竞争压力逐渐增大,无论走哪一条路,都不免需要一些相对丰富的算法知识,是故,便产生了一个寒假巩固速成算法的计划,可能对于像我这种算法竞赛小白而言,几乎很难,但我仍然还是想尝试一下,毕竟,梦想还是要有的,万一实现了呢?~( ̄▽ ̄~)~
为什么选择码蹄集作为刷题软件?
码蹄集 ,是在全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC) 指导下建设的,其依托全国各大名校计算机系和清华大学出版社等单位的强大资源,旨在为计算机学习爱好者提供全面和权威的计算机习题。
.
目录
一、单链表
(一)、单链表的定义
参考代码
cpp
//单链表的定义
typedef struct LNode{ //定义单链表结点类型
int data; //每个节点存放一个数据元素
struct LNode*next; //指针指向下一个节点
}LNode,*LinkList;
//增加一个新的结点:在内存中申请一个结点所需空间,并用指针p指向这个结点
LNode* p=(LNode*)malloc(sizeof(LNode));
//要表示一个单链表时,只需要声明一个头指针L,指向单链表的第一个结点
LinkList L; //声明一个指向单链表第一个结点的指针=LNode *L,但前者可读性更强
(二)、单链表的建立
cpp
//单链表的建立
//尾插法建立单链表
LinkList List_TailInsert(LinkList &L){ //正向建立单链表
int x; //设ElemType为整型
L = (LinkList)malloc(sizeof(LNode)); //建立头结点,初始化空表
LNode *s,*r=L; //r为表尾指针
scanf("%d",&x); //输入9999表示结束
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode)); //在r结点之后插入元素x
s->data=x;
r->next=s;
r=s; //r指向新的表尾结构。永远保持r指向最后一个结点
scanf("%d",&x);
}
r->next=NULL; //表尾指针置空
return L;
}
cpp
//头插法建立单链表------重要应用:链表的逆置
LinkList List_HeadInsert(LinkList &L){ //逆向建立单链表
LNode *s; int x;
L=(LinkList)malloc(sizeof(LNode)); //创建头结点
L->next=NULL; //初始为空链表
scanf("%d",&x); //输入9999表示结束
while(x != 9999){
s=(LNode*)malloc(sizeof(LNode)); //创建新结点
s->data=x;
s->next=L->next;
L->next=s; //将新结点插入表中,L为头指针
scanf("%d",&x);
}
return L;
}
cpp
//创建不带头结点的单链表
//初始化一个空的单链表
bool InitList(LinkList &L){
L = NULL; //空表,暂时还没有任何结点(防止脏数据)
return true;
}
bool Empty(LinkList L){ //判断单链表是否为空
return (L==NULL);
}
cpp
/初始化一个单链表(带头结点)
bool InitListWithNode(LinkList &L){
L = (LNode *) malloc(sizeof(LNode)); //分配一个头结点
if(L==NULL) //内存不足,分配失败
return false;
L->next = NULL; //头结点之后暂时还没有节点
return true;
}
//判断单链表是否为空(带头结点)
bool EmptyWithNode(LinkList L){
if(L->next==NULL)
return true;
else
return false;
}
//------------------------------------------------
//带头结点,写代码更方便
//不带头结点,写代码更麻烦
// (1)对第一个数据节点和后续数据结点的处理需要用不同的代码逻辑
// (2)对空表和非空表的处理需要用不同的代码逻辑
(三)、单链表的插入删除
cpp
//插入
//按位序插入(带头结点)
//在第i个位置插入元素e(带头结点)
bool ListInsertWithNode(LinkList &L,int i,int e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p==NULL) //i值不合法
return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s; //将结点s连到p之后
return true; //插入成功
} //平均时间复杂度O(n)
cpp
//按位序插入(不带头结点)
//插入、删除第1个元素时,需要改变头指针L
bool ListInsert(LinkList &L,int i,int e){
if(i<1)
return false;
if(i==1){ //插入第1个结点的操作与其他结点操作不同
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s; //头指针指向新结点
return true;
}
LNode *p; //指针p指向当前扫描的结点
int j=1; //当前p指向的是第几个结点
p=L; //p指向第1个结点(注意:不是头结点)
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
} //自此往下,=return InsertNextNode(p,e);
if(p==NULL) //i值不合法
return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true; //插入成功
}
cpp
//指定结点的后插操作
//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p,int e){
if(p==NULL)
return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data = e; //用结点s保存数据元素e
s->next = p->next;
p->next = s; //将结点s连到p之后
return true;
} //时间复杂度O(1)
cpp
//指定结点的前插操作
//前插操作(法一):在p结点之前插入元素e
bool InsertPriorNode(LNode *p,int e){
if(p==NULL)
return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->next=p->next;
p->next=s; //新结点s连到p之后
s->data=p->data; //将p中元素复制到s中
p->data=e; //p中元素覆盖为e
return true;
}
//前插操作(法二):在p结点之前插入元素e
bool InsertPriorNode(LNode *p,LNode *s){
if(p==NULL || s==NULL)
return false;
s->next=p->next;
p->next=s; //新结点s连到p之后
int temp = p->data; //交换数据域部分
p->data=s->data;
s->data=temp;
return true;
}
cpp
//删除操作
//删除表L中第i个位置的元素,并用e返回删除元素的值
//按位序删除(带头结点)
bool ListDeleteWithNode(LinkList &L,int i,int e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p==NULL) //i值不合法
return false;
if(p->next==NULL) //第i-1个结点之后已无其他结点
return false;
LNode *q=p->next; //令q指向被删除结点
e = q->data; //用e返回元素的值
p->next=q->next; //将*q结点从链中"断开"
free(q); //释放结点的存储空间
return true;
}
cpp
//删除指定结点p
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next; //令q指向*p的后继结点
p->data=p->next->data; //和后继结点交换数据域
p->next=q->next; //将*q结点从链中"断开"
free(q); //释放后继结点的存储空间
return true;
}
//单链表的局限性:
// (1)无法逆向检索,有时候不太方便
// (2)如果p是最后一个结点,只能从表头开始依次寻找p的前驱,时间复杂度O(n)
(四)、单链表的查找
cpp
//查找
//按位查找,返回第i个元素(带头结点)
LNode *GetElemWithNode(LinkList L,int i){
if(i<0)
return NULL;
LNode *p; //指针p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i){ //循环找到第i个结点
p=p->next;
j++;
}
return p;
} //平均时间复杂度O(n)
//王道书版本:
LNode *GetElemWithNodeCS(LinkList L,int i){
int j=1;
LNode *p=L->next;
if(i==0)
return L;
if(i<1)
return NULL;
while(p!=NULL && j<i){ //循环找到第i个结点
p=p->next;
j++;
}
return p;
} //平均时间复杂度O(n)
cpp
//按值查找,找到数据域==e的结点
LNode * LocateElem(LinkList L,int e){
LNode *p = L->next;
int i=0;
//从第1个结点开始查找数据域为e的结点
while(p!=NULL && p->data !=e){
p=p->next;
i++;
}
return p; //找到后返回该结点指针,否则返回NULL
} //平均时间复杂度O(n)
int LocateElemI(LinkList L,int e){
LNode *p = L->next;
int i=1;
//从第1个结点开始查找数据域为e的结点
while(p!=NULL && p->data !=e){
p=p->next;
i++;
}
if(p==NULL) return 0;
else return i; //找到后返回该元素位置,如果没找到,则返回0
} //平均时间复杂度O(n)
二、主函数代码
cpp
//求表的长度
int Length(LinkList L){
int len=0; //统计表长
LNode *p = L;
while(p->next != NULL){
p = p->next;
len++;
}
return len;
}
//------------------------------------------------------------
void printListWithNode(LinkList L){ //带头结点
LNode *p=L;
p=p->next;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
void printList(LinkList L){ //不带头结点
LNode *p=L;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
int main(){
LinkList L; //声明一个指向单链表的指针
InitListWithNode(L); //初始化一个空表(带头结点)
//InitList(L); //初始化一个空表(不带头结点)
//插入
ListInsertWithNode(L,1,3); //带头结点
ListInsertWithNode(L,2,4);
ListInsertWithNode(L,3,5);
printListWithNode(L);
//
// if(ListInsert(L,1,3)) //不带头结点
// printList(L);
//删除
ListDeleteWithNode(L,2,5);
printf("删除后的序列为:\n");
printListWithNode(L);
//查找
LNode *p;
p=GetElemWithNode(L,2); //按位查找
printf("第2个元素值为:%d\n",p->data);
int i = LocateElemI(L,4); //按值查找
if(i==0) printf("不存在该元素\n");
else printf("元素4所在位置为:%d\n",i);
//求单链表长度
int len = Length(L);
printf("该单链表的长度为:%d\n",len);
return 0;
}
三、结语
感谢大家一直以来的不断支持与鼓励,码题集题库中的进阶塔350题正在逐步更新 ,之后会逐步跟进星耀,王者的题,尽请期待!!!
同时,也希望这些题能帮助到大家,一起进步,祝愿每一个算法道路上的"苦行僧"们,都能够历经磨难,终成正果,既然选择了这条路,走到了这里,中途放弃,岂不是太过可惜?
另附中国计算机学会的杰出会员、常务理事轩哥博士的B站视频讲解链接https://space.bilibili.com/518554541/?spm_id_from=333.999.0.0,供大家更好的进行学习与刷题~( ̄▽ ̄~)~
愿你的结局,配得上你一路的颠沛流离。