[学习笔记]循环链表

文章首发:http://pjf.name/post-111.html

本文基于"姓名标识-非商业性-相同方式分享 4.0国际"协议创作或转载,转载原创文章请注明来源于疯子的自留地,否则请勿转载或再转载,谢谢合作:)

前面复习了单链表,接着复习循环链表.这里以单链表做示范.

      什么是循环链表呢?一般的链表中尾结点的next指向的是NULL值,如果我们现在指向最后一个结点,当我们需要指向第一个结点时就只有重新从头结点开始,浪费了时间,现在我们把尾结点的next指向头结点,这样EndNode->next就是FirstNode的地址了不是?这就叫循环链表.

      <<大话>>中说循环链表中不用头指针了,改为尾指针,即它的next值指向尾结点而非头结点.这样Header->next->next就是头结点了,确实很方便,但是依旧用头指针可以吗?我们首先来看下,如果头指针依旧指向头结点,那么我们会发现,如果我们在一个链表处插入一个新的头结点,那么按照循环链表的定义来说,我们应该让尾结点指向这个新的头结点,但是问题出来了,尾结点的内存地址是多少呢?不知道.同样的,如果删除头结点也会出现这个问题.因此,循环链表不用头指针而改用尾指针就解决了这个问题.这也是我自己在练习的时候发现的,困扰我的问题也就迎刃而解了.

      当然,循环链表属于链表的子类,依旧有遍历,整表创建,整表删除,插入,删除,查找操作.以下是相关代码:

链表结构:

1 typedef struct Node{
2     char Data;
3     struct Node *next;
4 }Node;
5 typedef struct Node *List;

1.整表插入

01 BOOL CreateCircularList(List *L,int Length)//循环列表的整表创建
02 {
03     List EndNode,NewEndNode,FirstNode;
04     int temp;
05     char Chart='a';
06  
07     *L=(List)malloc(sizeof(Node));//为尾指针分配内存
08     if(!(*L))
09         return false;
10     EndNode=*L;
11     for(temp=0;temp<Length;temp++,Chart++)
12     {
13         NewEndNode=(List)malloc(sizeof(Node));//为新的尾结点分配内存
14         if(!NewEndNode)
15             return false;
16         NewEndNode->Data=Chart;//为新的尾结点赋予相应的值
17         EndNode->next=NewEndNode;//旧的尾结点的next指向新的尾结点的地址
18         EndNode=NewEndNode;//把EndNode后移一位
19         if(0==temp) //保存头结点的信息
20             FirstNode=EndNode;
21     }
22     EndNode->next=FirstNode;//尾结点的next指向头结点
23     (*L)->next=EndNode;//尾指针的next指向尾结点
24     (*L)->Data=temp;//保存链表的长度
25  
26     return true;
27 }

2.整表删除

01 BOOL EmptyCircularList(List *L)//循环链表的整表删除
02 {
03     List DeleteNode,EndNode;
04     int temp;
05  
06     if(0==(*L)->Data)
07         return false;//当循环链表为空时,返回false
08  
09     EndNode=(*L)->next;//取得尾结点的地址
10  
11     for(temp=1;temp<(int)((*L)->Data);temp++)//从首节点开始删除,一直到尾结点的前一个结点
12     {
13         DeleteNode=EndNode->next;//取得要删除的结点(即头结点)
14         EndNode->next=DeleteNode->next;//把头结点的next结点改为新的头结点
15         free(DeleteNode);//释放旧的头结点的内存
16     }
17  
18     free(EndNode);//释放尾结点的内存
19     (*L)->next=NULL;//头指针指向NULL
20     (*L)->Data=0;//链表长度改为0
21      
22     return true;
23 }

3.插入操作

01 BOOL InsertDataCircularList(List *L,int InsertLocation,char InsertData)
02 {
03     List InsertNodePre,InsertNode;
04     int temp;
05  
06     if(InsertLocation<1)//如果插入的位置小于1,则默认新插入的数据为首结点
07         InsertLocation=1;
08     if(InsertLocation>((int)(*L)->Data))//如果插入的位置大于链表的长度,则默认新插入的位数为尾结点
09         InsertLocation=(*L)->Data;
10  
11     InsertNodePre=*L;//将将新增结点的前一个结点设为头指针,原因见删除结点操作的该语句
12     for(temp=0;temp<InsertLocation;temp++)//将InsertNodePre指向欲新增的结点的前一个结点
13     {
14         InsertNodePre=InsertNodePre->next;//结点后移一位
15     }
16  
17     InsertNode=(List)malloc(sizeof(Node));//为要插入的结点分配内存
18     if(!InsertNode)
19         return false;
20     InsertNode->Data=InsertData;//给插入的结点的数据域赋值
21     InsertNode->next=InsertNodePre->next;//指定要插入的结点next值
22     InsertNodePre->next=InsertNode;//将要插入的结点的前一个结点的next指向这个插入的结点
23  
24     if((*L)->Data==temp+1)//如果这个新插入的结点为新的尾结点,则更新尾指针的next值
25         (*L)->next=InsertNode;
26     (*L)->Data++;//链表的长度加1
27  
28     return true;
29 }

4.删除操作

01 BOOL DeleteDataCircularList(List *L,int DeleteLocation,char *OutputDeleteValue)
02 {
03     int temp;
04     List DeleteNodePre,DeleteNode,DeleteNodeNext;
05  
06     if(DeleteLocation<1)//如果删除的位置小于1,则将删除位置设为头结点
07         DeleteLocation=1;
08     if(DeleteLocation>((int)(*L)->Data))//如果删除的位置大于链表长度,则将删除位置设置为尾结点
09         DeleteLocation=((int)(*L)->Data);
10  
11     DeleteNodePre=*L;//首先将结点首先设置为头指针,为什么是头指针而不是头结点或者尾结点呢,因为如果是头结点下面的for循环的话就是删除正确的删除结点后的第2个结点了,而如果设为尾结点的话则是正确的删除结点后的那个结点
12      
13     for(temp=0;temp<DeleteLocation;temp++)//将结点位置设置为与删除的前一个结点
14     {
15         DeleteNodePre=DeleteNodePre->next;//结点后移一位
16     }
17  
18     DeleteNode=DeleteNodePre->next;
19     DeleteNodeNext=DeleteNode->next;
20     DeleteNodePre->next=DeleteNodeNext;
21     *OutputDeleteValue=DeleteNode->Data;
22     free(DeleteNode);//释放欲删除结点的内存
23  
24     if(temp==((int)(*L)->Data))//如果删除的节点是尾结点,那么我们让尾指针指向新的尾结点
25         (*L)->next=DeleteNodePre;
26     (*L)->Data--;//链表的结点数减去1
27  
28     return true;
29 }

5.遍历链表

01 BOOL LookUpCircularList(List L)
02 {
03     List CurrentNode;
04     int count=1;
05  
06     if(0==L->Data)//如果链表为空,返回错误
07         return false;
08     CurrentNode=L->next->next;//当前结点指向链表头结点
09     while((L->next)!=CurrentNode)//不断输出当前结点的数据域的值
10     {
11         printf("%3c",CurrentNode->Data);
12         if(0==count%6)
13             printf("\n");
14         CurrentNode=CurrentNode->next;//结点后移
15         count++;
16     }
17     printf("%3c\n",CurrentNode->Data);//输出尾结点的值
18  
19     return true;
20 }

然后用一个小程序跑一下,以下是代码:

01 int main(void)
02 {
03     List Cl;
04     char InsertData,DeleteData;
05     int InsertPosition,DeletePosition;
06  
07     if(true==CreateCircularList(&Cl,26))
08         printf("循环链表创建成功!");
09     else
10         return false;
11     printf("现在链表数据如下:\n");
12     LookUpCircularList(Cl);
13     printf("请输入要插入的字符:");
14     scanf("%c",&InsertData);
15     fflush(stdin);
16     printf("请输入要插入的位置:");
17     scanf("%d",&InsertPosition);
18     if(true==InsertDataCircularList(&Cl,InsertPosition,InsertData))
19         printf("插入数据成功!插入的字符为%c,",InsertData);
20     else
21         return false;
22     printf("新的链表数据如下:\n");
23     LookUpCircularList(Cl);
24     printf("请输入要删除的数据的位置:");
25     if(1==scanf("%d",&DeletePosition))
26         DeleteDataCircularList(&Cl,DeletePosition,&DeleteData);
27     else
28         return false;
29     printf("删除数据成功!删除的字符为:%c\n",DeleteData);
30     fflush(stdin);
31     printf("是否清空链表?1代表清空,0代表不清空:");
32     scanf("%d",&DeletePosition);
33     if(1==DeletePosition)
34     {
35         if(true==EmptyCircularList(&Cl))
36             printf("清空数据成功!现在链表长度为%d\n",Cl->Data);
37     }
38     else
39         printf("数据未清空!链表长度为%d\n",Cl->Data);
40     fflush(stdin);
41     getchar();
42     return 0;
43 }

运行结果

Capture.PNG

      当然,有了循环链表,还有双向链表,时间不早了,明天再回顾

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值