循环链表

来源:互联网 发布:德哈维兰 知乎 编辑:程序博客网 时间:2024/05/22 14:45

小编最近在学习循环链表,将自己写的循环链表的函数上传,请大家多多指点。

头文件clist.h

//clist.h# pragma once//循环链表typedef struct CNode{int data;struct CNode *next;}CNode, *CList;//初始化函数void InitCList(CList plist);//头插函数void Insert_head(CList plist,int val);//尾插函数void Insert_tail(CList plist,int val);//按指定位置插入值的函数bool Insert_pos(CList plist,int pos,int val);//查找函数CNode *Search(CList plist,int key);//删除函数bool Delete(CList plist,int key);//打印函数void Show(CList plist);//逆置函数1void Reverse(CList plist);//利用头插方法实现函数的逆置void Reverse1(CList plist);//得到链表有效长度的函数int GetLength(CList plist);//清理函数void Clear(CList plist);//摧毁函数void Destroy(CList plist);//判断链表是否为空的函数bool IsEmpty(CList plist);
函数实现部分 clist.cpp

# include "clist.h"# include <stdio.h># include <stdlib.h># include <assert.h>//初始化函数void InitCList(CList plist){//循环函数,将头结点的 next 指向自己。plist->next = plist;}//插入节点时需创建新的节点,此函数不对外提供服务,加上static关键字static CNode *BuyNode(int val){CNode *p = (CNode *)malloc(sizeof(CNode));assert(p != NULL);p->data = val;//p->next = p; 不用每次新创建的节点都指向自己,头尾相接即可。return p;}//头插,使用头插函数时,后插入的 data 排在前面,是个逆序排列,不符合人们使用习惯void Insert_head(CList plist,int val){assert(plist != NULL);CNode *p = BuyNode(val);//将头结点后面的数据先绑好p->next = plist->next;//头结点的 next 指向 p,实现头插plist->next = p;}//尾插,使用尾插符合人们使用规律,后插入的 data 在尾巴处void Insert_tail(CList plist,int val){CNode *p = BuyNode(val);CNode *q;//先找到尾巴,注意循环链表循环终止条件: q->next!=plistfor(q=plist; q->next!=plist; q=q->next){;}//将尾巴的 next 赋值给新节点的 next ,绑好尾巴p->next = q->next;//实现尾插q->next = p;}//在指定位置插入数据bool Insert_pos(CList plist,int pos,int val){//一定进行参数判断,出错处理assert(plist != NULL);if(pos<0 || pos>GetLength(plist)){return false;}CNode *p = plist;CNode *q = BuyNode(val);//找到要插入的位置for(int i=0; i<pos; i++,p=p->next){;}//插入数据q->next = p->next;p->next = q;return true;}//查询函数,找到需查询的数据,返回该数据节点CNode *Search(CList plist,int key){for(CNode *p=plist->next; p!=plist; p=p->next){if(key == p->data){return p;}}return NULL;}//查找前驱节点,为实现 Delete 函数。static CNode *Serach_Prio(CList plist,int key){for(CNode *p=plist; p->next!=plist; p=p->next){// p->next->data,因为是查找前驱函数,此处不担心崩溃,因为循环中已判断 p->next 不会为NULLif(key == p->next->data){return p;}}return NULL;}//删除函数bool Delete(CList plist,int key){//查找被删节点的前驱节点CNode *p = Serach_Prio(plist, key);//没找到,则不删除if(p == NULL){return false;}//找到后,前驱节点的 next 指向被删除节点的下一个节点的 next ,剔除被删除节点后freeCNode *q = p->next;p->next = q->next;free(q);return true;}//打印函数void Show(CList plist){for(CNode *p=plist->next; p != plist; p=p->next){printf("%d ", p->data);}printf("\n");}//逆置函数void Reverse(CList plist){//没有数据、只有头结点、只有一个节点这三种情况不需要逆置if(plist == NULL || plist->next == plist || plist->next->next == plist){return;}//定义三个指针p, q, s. s 保存正常节点的第一个,p 和 q 指针实现逆置CNode *p = plist->next;CNode *q = plist->next->next;CNode *s;p->next = plist;while(q != plist){s = q->next;q->next = p;p = q;q = s;}plist->next = p;}//利用头插的性质实现循环链表的逆置void Reverse1(CList plist){assert(plist != NULL);CNode *p = plist->next;CNode *q;plist->next = plist;//进行头插while(p != plist){q = p->next;p->next = plist->next;plist->next = p;p = q;}}//得到链表有效长度的函数int GetLength(CList plist){int count = 0;for(CNode *p=plist->next; p != plist; p=p->next){count++;}return count;}//清理函数,调用Destory函数即可void Clear(CList plist){Destroy(plist);}//摧毁函数,每次释放第一个数据节点void Destroy(CList plist){CNode *p;while(plist->next != plist){p = plist->next;plist->next = p->next;free(p);}}//判断链表是否为空bool IsEmpty(CList plist){return plist->next == plist;}
测试函数示例test.cpp

# include "clist.h"# include <stdio.h># include <vld.h>int main(){CNode head;InitCList(&head);for(int i=0; i<8; i++){Insert_tail(&head, i);}Show(&head); Reverse1(&head);//Insert_pos(&head, 0, -1);//Insert_pos(&head, 8, 8);//Insert_pos(&head, 4, 10);//Insert_pos(&head, -1, -1);//Insert_pos(&head, 16, -1);Show(&head); //Delete(&head, 0);//Show(&head); //printf("%d\n", GetLength(&head));Destroy(&head);Destroy(&head);return 0;}

测试函数经过删减。


1 0
原创粉丝点击