1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > (C语言版)链表(四)——实现双向循环链表创建 插入 删除 释放内存等简单操作

(C语言版)链表(四)——实现双向循环链表创建 插入 删除 释放内存等简单操作

时间:2021-05-29 06:30:11

相关推荐

(C语言版)链表(四)——实现双向循环链表创建 插入 删除 释放内存等简单操作

/fisherwan/article/details/19801993

双向循环链表是基于双向链表的基础上实现的,和双向链表的操作差不多,唯一的区别就是它是个循环的链表,通过每个节点的两个指针把它们扣在一起组成一个环状。所以呢,每个节点都有前驱节点和后继节点(包括头节点和尾节点)这是和双向链表不同的地方。我们看下双向循环链表的示意图(我在网上找了张图片,自己画的实在难看,有时间真的要去学下怎么画图了,然后可以写出更好的博客):

在程序的编写方面呢,双向循环链表有点向前面知识的组合,双向循环链表在“双向”方面和双向链表一样,在“循环方面”和单向循环链表一样。以前的知识消化了呢,这个双向循环链表也就简单了。上面这个图比较形象的体现出了双向循环链表的含义:简单说呢,就是当前节点的一个指针指向前置节点一个指针指向后继节点,每个节点都重复这样,就形成了一个环了。

DbCcLinkList.h 头文件——包含了节点结构的定义和链表相关操作函数的声明

[cpp]view plaincopy#ifndefDOUBLE_CIRCULAR_LINKED_LIST_H #defineDOUBLE_CIRCULAR_LINKED_LIST_H typedefstructNode { intdata; structNode*pNext; structNode*pPre; }NODE,*pNODE; //创建双向循环链表 pNODECreateDbCcLinkList(void); //打印链表 voidTraverseDbCcLinkList(pNODEpHead); //判断链表是否为空 intIsEmptyDbCcLinkList(pNODEpHead); //计算链表的长度 intGetLengthDbCcLinkList(pNODEpHead); //向链表中插入节点 intInsertEleDbCcLinkList(pNODEpHead,intpos,intdata); //从链表中删除节点 intDeleteEleDbCcLinkList(pNODEpHead,intpos); //删除整个链表,释放内存 voidFreeMemory(pNODE*ppHead); #endif

DbCcLinkList.cpp 双向循环链表的源文件——包含了链表相关操作函数的定义

(1)这部分是用来创建链表的,双向循环链表每插入一个节点就要控制4个指针,第一,插入位置的上一个节点有一个指针,它要指向插入节点;第二,插入的节点有两个指针,一个指向上一个节点,一个指向下一个节点;第三,插入位置的下一个节点有一个指针,它是指着插入节点的。写程序的关键也就是控制好这四个指针,不要弄错了,要不然会有奇怪的结果(程序出不来结果,无线循环等)

[cpp]view plaincopy#include<stdio.h> #include<stdlib.h> #include"DbCcLinkList.h" //创建双向循环链表 pNODECreateDbCcLinkList(void) { inti,length=0,data=0; pNODEp_new=NULL,pTail=NULL; pNODEpHead=(pNODE)malloc(sizeof(NODE)); if(NULL==pHead) { printf("内存分配失败!\n"); exit(EXIT_FAILURE); } pHead->data=0; pHead->pNext=pHead; pHead->pPre=pHead; pTail=pHead; printf("请输入想要创建链表的长度:"); scanf("%d",&length); for(i=1;i<length+1;i++) { p_new=(pNODE)malloc(sizeof(NODE)); if(NULL==p_new) { printf("内存分配失败!\n"); exit(EXIT_FAILURE); } printf("请输入第%d个节点元素值:",i); scanf("%d",&data); p_new->data=data; p_new->pPre=pTail; p_new->pNext=pHead; pTail->pNext=p_new; pHead->pPre=p_new; pTail=p_new; } returnpHead; } (2)这部分是获得双向链表的信息,和单向循环链表一样,链表结束的限制条件是判断是否等于头结点。意思就是从头节点的下一个节点开始如果又到了头节点说明已经遍历一圈了。 [cpp]view plaincopy//打印链表 voidTraverseDbCcLinkList(pNODEpHead) { pNODEpt=pHead->pNext; printf("链表打印如:"); while(pt!=pHead) { printf("%d",pt->data); pt=pt->pNext; } putchar('\n'); } //判断链表是否为空 intIsEmptyDbCcLinkList(pNODEpHead) { pNODEpt=pHead->pNext; if(pt==pHead) return1; else return0; } //计算链表的长度 intGetLengthDbCcLinkList(pNODEpHead) { intlength=0; pNODEpt=pHead->pNext; while(pt!=pHead) { length++; pt=pt->pNext; } returnlength; }

(3)这部分是双向链表的插入、删除节点操作,但是它不需要和双向链表一样判断最后一个节点是否为空(因为此时要用到节点的指针),双向循环链表不存在这样的情况,这是由于它不光是双向的,而且是循环的,所以每个节点都不可能为空。 [cpp]view plaincopy//向链表中插入节点 intInsertEleDbCcLinkList(pNODEpHead,intpos,intdata) { pNODEp_new=NULL,pt=NULL; if(pos>0&&pos<GetLengthDbCcLinkList(pHead)+2) { p_new=(pNODE)malloc(sizeof(NODE)); if(NULL==p_new) { printf("内存分配失败!\n"); exit(EXIT_FAILURE); } while(1) { pos--; if(0==pos) break; pHead=pHead->pNext; } p_new->data=data; pt=pHead->pNext; p_new->pNext=pt; p_new->pPre=pHead; pHead->pNext=p_new; pt->pPre=p_new; return1; } else return0; } //从链表中删除节点 intDeleteEleDbCcLinkList(pNODEpHead,intpos) { pNODEpt=NULL; if(pos>0&&pos<GetLengthDbCcLinkList(pHead)+1) { while(1) { pos--; if(0==pos) break; pHead=pHead->pNext; } pt=pHead->pNext->pNext; free(pHead->pNext); pHead->pNext=pt; pt->pPre=pHead; return1; } else return0; }

(4)这是释放内存操作,和上面讲的一样,不需要判断最后一个节点是否为空,每次释放一个节点的内存的以后它还是保持环状的结构,所以没有节点为空。 [cpp]view plaincopy//删除整个链表,释放内存空间 voidFreeMemory(pNODE*ppHead) { pNODEpt=NULL; while(*ppHead!=NULL) { pt=(*ppHead)->pNext->pNext; if((*ppHead)->pNext==*ppHead) { free(*ppHead); *ppHead=NULL; } else { free((*ppHead)->pNext); (*ppHead)->pNext=pt; pt->pPre=*ppHead; } } }

maincpp 测试程序——通过简单的交互界面判断函数的功能是否正确 [cpp]view plaincopy#include<stdio.h> #include<stdlib.h> #include"DbCcLinkList.h" intmain(void) { intflag=0,length=0; intposition=0,value=0; pNODEhead=NULL; head=CreateDbCcLinkList(); flag=IsEmptyDbCcLinkList(head); if(flag) printf("双向循环链表为空!\n"); else { length=GetLengthDbCcLinkList(head); printf("双向循环链表的长度为:%d\n",length); TraverseDbCcLinkList(head); } printf("请输入要插入节点的位置和元素值(两个数用空格隔开):"); scanf("%d%d",&position,&value); flag=InsertEleDbCcLinkList(head,position,value); if(flag) { printf("插入节点成功!\n"); TraverseDbCcLinkList(head); } else printf("插入节点失败!\n"); flag=IsEmptyDbCcLinkList(head); if(flag) printf("双向循环链表为空,不能进行删除操作!\n"); else { printf("请输入要删除节点的位置:"); scanf("%d",&position); flag=DeleteEleDbCcLinkList(head,position); if(flag) { printf("删除节点成功!\n"); TraverseDbCcLinkList(head); } else printf("删除节点失败!\n"); } FreeMemory(&head); if(NULL==head) printf("已成功删除双向循环链表,释放内存完成!\n"); else printf("删除双向循环链表失败,释放内存未完成!\n"); return0; }

PS:到这里为止,链表的知识就写到这里了,后面开始就是队列和栈了。其实线性表方面的知识都大同小异,只要原理清楚之后,它的形式的变化可以是多钟多样的,当然本人水平有限,希望能和同道之人继续深入探讨,共同进步。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。