链表是实现存储线性表的一种常用存储结构,这种存储结构不要求逻辑关系上相邻的两个元素在物理位置上也相邻存储,而是通过增加指针来表示元素之间的逻辑关系和后继元素的位置。
线性表的链接的表示的特点是,用一组可以是不连续的存储单元来存储线性表中的各个元素,为了表示元素与其后继元素之间的的逻辑关系,每个元素除了要存储自身的信息外,还需要存储一个指示后继的信息(即存储后继元素的存储位置)。这样每个节点就包括两个域:数据域——存放元素本身的信息,指针域——存续后继元素的存储位置。
以上则为链表结构的简单原理介绍,我们今天侧重是分享一下关于链表的简单代码实现。
目录
一、创建一个简单的链表
这里以创建一个数量具有十个节点,节点存储int类型的数据为例
LinkList create_link(void) // 使用成功 返回该链表的头指针
{
/*
1、创建头指针,初始化为null
2、连接节点,count控制节点数量
3、最后节点定义指向null
*/
int count=10; //计数器
int data=1; //便于操作,结构体存放的数据类型为int
LinkList llist=(LinkList)malloc(sizeof(struct Node));
llist->info=20220403; //随便输入一个大数据,便于区分头结点
LinkList temp=llist;
if(llist!=NULL) llist->link=NULL; //生成头结点指向null
else {
printf("Error!") ;
return llist; //创建失败,报错返回
}
while(count>0) //生成并连接十个节点
{
PNode p=(PNode)malloc(sizeof(struct Node)); //创建节点并分配空间
p->link=NULL;
p->info=data;
llist->link=p;
llist=p;
data++; //更新下一个节点的数据
count--;
}
free(llist->link);
llist->link=NULL;
llist=temp;
return llist;
}
二、遍历链表
前面说到,没给节点其均存放着两个域,一个是数据域,用于存放其自身的元素数据,还有另一个则是指针域,用于存放下一个节点的存储地址。所以说,每一个节点都是知道下一个节点的存储地址的,所以,我们只需获取了头指针,即可获取到整条链表的信息。
int Print_link(LinkList llist) //使用成功 遍历输出链表
{
PNode p=NULL;
if(llist==NULL) return 0; //空链表,正常退出
p=llist ;
while(p!=NULL)
{
printf("节点地址:%p 指针域: %p 数据域:%d\n",p,p->link,p->info);
p=p->link;
}
printf("输出完毕!\n");
}
结合前面说的创建链表,我们来简单测试一下是否正常运行。
成功运行!
三、在指定节点后面添加存储有数据x的节点
要实现这一操作,首先我们需要将其数据x封装成与链表节点类型相同,第二步新节点中的指针域中存放指定节点的后继节点地址,第三步则是要将新节点的地址存入指定节点中。如下是简单示意图
代码实现
int Test_insert_Link(LinkList llist,PNode p,int x) //测试成功 在节点之后添加数据为
// x的节点
{
if(llist==NULL || p==NULL) return -1;
PNode q;
q=(PNode)malloc(sizeof(struct Node));
q->info=x;
q->link=p->link;
p->link=q;
return 1;
}
四、在指定节点p前面连接新节点
节点前面连接新节点,与在节点后面连接新节点稍有不同。由于在单链表中,只有指向后继节点的指针,所以只有首先找到p所指节点的前驱节点之后,然后再调用前面所提的在节点后面加入新节点的方法实现。
int Test_Front_insert_link (LinkList llist,PNode p,int x) //测试成功 在前驱结点前加入数据
// 为x的的新节点
{
if(llist==NULL) return -1;
PNode q=llist,t; //t节点用于封装x数据作为新节点
t=(LinkList)malloc(sizeof(struct Node));
t->info=x;
while(q!=NULL && q->link != p)
{
q=q->link; //寻访前驱节点
}
t->link=q->link; //连接进链表
q->link=t;
return 1;
}
五、删除链表指定节点p
删除节点p的思路为,寻找p的前驱节点pre_p使其指针域由原先存放着p节点的地址,改为存放p的一个节点的地址即可,即pre_p->link=p->link
代码实现
int Test_Delete_Link(LinkList llist,int x) //测试成功 删除指定节点
{
PNode p=llist,q=llist;
if(llist==NULL) return -1;
while(p!=NULL&&p->info!=x)
{
p=p->link; //寻找数据x对应的节点
}
while(q->link!=NULL && q->link!=p)
{
q=q->link; //寻找p的前前驱节点
}
q->link=p->link;
free(p);
return 1;
}
六、创建循环链表
与单链表相比,循环链表没有增加新的存储空间,但是,从循环链表的任意节点出发,都可以访问所有节点。创建循环链表的思路也很简单,将单链表的形式稍加改变,不让最后一节节点的指针为NULL,而让它指向第一个节点,这样就得到了一个循环链表。
LinkList create_CycleLink(void) // 使用成功 定义生成循环链表
{
int count=10; //计数器
int data=1; //便于操作,结构体存放的数据类型为int
LinkList llist=(LinkList)malloc(sizeof(struct Node));
llist->info=20220403;
LinkList temp=llist;
if(llist!=NULL) llist->link=NULL; //生成头结点指向null
else {
printf("Error!") ;
return llist; //创建失败,报错返回
}
while(count>0) //生成并连接十个节点
{
PNode p=(PNode)malloc(sizeof(struct Node)); //创建节点并分配空间
p->link=NULL;
p->info=data;
llist->link=p;
llist=p;
data++; //更新下一个节点的数据
count--;
}
free(llist->link);
llist->link=temp; //将最后一个节点的指针指向第一个节点
return llist;
}
七、删除链表中从下标i开始连续k个节点
这一个实现只需要在前面删除一个节点的算法思路上稍加改变即可,不多说直接上代码!
int Test_dele_k_FromList(LinkList llist,int i,int k) //测试成功
{
if(llist==NULL) return -1;
PNode p,q=llist;
int count =0,temp=0;
while(count<i-1 && q->link!=NULL)
{
q=q->link;
count++;
}
p=q->link; //p为要求被删除的第一个节点
while(p->link!=NULL&&temp<k)
{
q->link=p->link;
p=p->link;
temp++;
}
return 1;
}
八、删除单链表中数据重复的节点
删除单链表中值相同的多余的节点,即若链表中有多个节点的数据域相同,只保留一个节点,其余节点从链表中删去,使得最后得到的链表各个节点的数据域均不相同。
那我们思路应该如何呢?其实就是两点,一个是找出数据域重复的节点,一个是删去该节点。
int Test_DeleteMoreList(LinkList llist)
{
PNode p,q;
p=q=llist;
int pp;
for(int i=0;i<Count_Link(llist)-1&&p!=NULL;i++)
{
q=p->link;
for(int j=i+1;j<Count_Link(llist)-1&&q!=NULL;j++)
{
if(p->info==q->info)
{
PNode pre_q=llist;
while(pre_q!=NULL && pre_q->link != q)
{
pre_q=pre_q->link; //寻访前驱节点
}
pre_q->link= q->link;
q=pre_q->link;
}else{
q=q->link;
}
}
p=p->link;
}
}
以上就是一些基础的链表操作了!