循环链表,在严蔚敏《数据结构》第35页中做了简略的提及 ,没在算法程序方面做过多的讲解。循环链表的特点是最后一个结点的指针域指向头结点,整个链表形成一个环。由此,从表中任一结点出发均可找到表中其他结点。(P35)
单链的循环链表结点的存储结构和单链表结点的存储结构一样。这两者的不同点在于,循环链表的最后一个结点的next域指向头结点,而不是“空” NULL。这样做的原因在于可以很容易的找到表头。但若链表较长,则由表头找到表尾较为费时。因而,单循环链表往往设立尾指针而不是头指针。同时也是为了简化操作。例如将两个线性表合并一个表时,仅需将一个表的表尾和另一个表的表头相接
下面的源程序是设立尾指针的单循环链表的基本操作:
#include<stdio.h> #include<malloc.h> #include<math.h> #include<stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 typedef int ElemType; typedef int Status; //构建带头结点的循环链表 struct LNode{ ElemType data; LNode *next; }; typedef LNode *LinkList; void InitList(LinkList &L){ //操作结果:构造一个空的线性表 L=(LinkList)malloc(sizeof(LNode));//产生头结点,并使L指向此头结点 if(!L)//存储分配失败 exit(OVERFLOW); L->next=L;//头结点的指针域指向头结点,与单链表的不同之处 } Status ListInsert(LinkList &L,int i,ElemType e){ //在循环链表L中第i个位置之前插入元素e int j=0; LinkList s,p=L->next;//p指向头结点 if(i<=0||i>ListLength(L)+1)//i值不合法 return ERROR; while(j<i-1){ j++;//计数器+1 p=p->next;//p指向下个结点 } s=(LinkList)malloc(sizeof(LNode));//生成新结点,以下将其插入L中 s->data=e;//将e赋给新结点 s->next=p->next;//新结点指向元第i个结点 p->next=s;//原第i-1个结点指向新结点 if(p==L)//尾指针判断 L=s;//L指向新的尾结点 return OK;//插入成功 } void print(ElemType e){ printf("%d ",e); } void ListTraverse(LinkList L,void (*visit)(ElemType )){ //初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数visit() LinkList p=L->next; while(p)//p所指结点存在 { visit(p->data);//对p所指结点调用函数visit() p=p->next;//p指向下一个结点 } printf("\n"); } Status ListEmpty(LinkList L){ //初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE if(L->next==L)//空 return TRUE; else return FALSE; } int ListLength(LinkList L){ //初始条件:线性表L已存在。操作结果;返回L中数据元素的个数 LinkList p; int i=0;//计数器初值为0 p=L->next;//p指向第1个结点 while(p!=L){//未到表尾 i++;//计数器+1 p=p->next;//p指向下个结点 } return i; } void DestroyList(LinkList &L){ //初始条件:线性表L已存在。操作结果:销毁线性表L ClearList(L);//将表L重置为空表 free(L);//释放L所指结点(头结点) L=NULL;//L不指向任何存储单元 } void ClearList(LinkList L){ //初始条件:线性表L已存在。操作结果:将L重置为空表(只留下头指针和头结点) LinkList p,q; L=L->next;//L指向头结点 p=L->next;//p指向第一个结点 while(p!=L){ q=p->next;//q指向p的后继结点 free(p);//释放p所指结点 p=q;//p指向q所指结点 } L->next=L;//头结点指针域指向自身 } int LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)){ //初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) //操作结果:返回L中第1个与e满足关系compare()的数据元素的位序 //若这样的数据元素不存在,则返回值为0 int i=0; LinkList p=L->next->next;//p指向第1个结点 while(p!=L->next)//p未指向头结点 { i++; if(compare(p->data,e))//找到这样的数据元素 return i; p=p->next;//p指向下一个结点 } return 0;//满足关系的数据元素不存在 } Status equal(ElemType c1,ElemType c2){ //判断是否相等的函数 if(c1==c2) return TRUE; else return FALSE; } Status GetElem(LinkList L,int i,ElemType &e){//算法2.8 //L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回ok,否则返回ERROR int j=1;//计数器初值为1 LinkList p=L->next->next;//p指向第1个结点 if(i<=0||i>ListLength(L))//第i个元素不存在 return ERROR; while(j<i) { j++; p=p->next;//p指向下一个结点 } e=p->data;//取第i个元素的值赋给e return OK; } Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e){ //初始条件:线性表L已存在 //操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回ok,否则操作失败,pre_ew无定义,返回ERROR LinkList q,p=L->next->next; q=p->next;//q指向p的后继 while(q!=L->next){//p未到表尾(q未指向头结点) if(q->data==cur_e)//p的后继为cur_e { pre_e=p->data;//将p所指元素的值赋给pre_e return OK;// } p=q; //p的后继不为cur_e,p向后移 q=p->next;//q指向p的后继 } return ERROR; } Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e){ //初始条件:线性表L已存在 //操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e 返回它的后继,返回OK LinkList p=L->next->next;//p指向第一个结点 while(p!=L) //p未到表尾 { if(p->data==cur_e)//p所指结点的值为cur_e { next_e=p->next->data;//将p所指结点的后继结点的值赋给next_e return OK; } p=p->next;//p指向下个结点 } return ERROR; } Status ListDelete(LinkList L,int i,ElemType &e){//算法2.10 不改变L //在带头结点的单链线性表L中,删除第i个元素,并由e返回其值 int j=0;//计数器初值为0 LinkList q,p=L->next;//p指向头结点 if(i<=0&&i>ListLength(L))//第i个元素不存在 return ERROR; while(j<i-1){//寻找第i-1个结点 j++; p=p->next;//p指向下一个结点 } q=p->next; p->next=q->next;//待删除结点的前驱指向待删结点的后继 e=q->data;//将待删结点的值赋给e if(L==q)//删除的是表尾元素 L=p;//L指向新的表尾元素 free(q);//释放待删结点 return OK;//删除成功 } void main(){ LinkList L;//首先定义指针L ElemType e,e0; Status i; int j,k; InitList(L);//初始化线性表 for(j=1;j<=5;j++) i=ListInsert(L,1,j);//从L的表头插入j printf("在L的表头依次插入1~5后,L="); ListTraverse(L,print);//依次对元素调用print(),输出元素的值 i=ListEmpty(L);//检测表L是否为空 printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L)); ClearList(L);//清空表L printf("清空L后,L="); ListTraverse(L,print); i=ListEmpty(L);//检测表L是否为空 printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L)); for(j=1;j<=10;j++) ListInsert(L,j,j);//在L的表尾插入j printf("在L的表尾依次插入1~10后,L= "); ListTraverse(L,print);//依次输出表L中的元素 for(j=0;j<=1;j++) k=LocateElem(L,j,equal);//查找表L中与j相等的元素,并将其在链表中的排序赋给k if(k) //k不为0,表明有符合条件的元素 printf("第%d个元素的值为%d\n",k,j); for(j=1;j<=2;j++)//测试头2个数据 { GetElem(L,j,e0);//把表L中的第j个数据赋给e0 i=PriorElem(L,e0,e);//把表L中的第j个数据赋给e0 if(i==ERROR) printf("元素%d无前驱\n",e0); else printf("元素%d的前驱为%d\n ",e0,e); } for(j=ListLength(L)-1;j<=ListLength(L);j++) //最后2个数据 { GetElem(L,j,e0);//把表L中的第j个数据赋给e0 i=NextElem(L,e0,e);//求e0的后继,如成功,将值赋给e if(i==ERROR) printf("元素%d无后继\n",e0); else printf("元素%d有后继%d\n",e0,e); } k=ListLength(L);//k为表长 for(j=k+1;j>=k;j--) { i=ListDelete(L,j,e);//删除第j个数据 if(i==ERROR)//表中不存在第j个数据 printf("删除第%d个元素失败(不存在此元素)。",j); else //表中存在第j个数据,删除成功,其值赋给e printf("删除第%d个元素成功,其值为%d\n",j,e); } printf("依次输出L的元素:"); ListTraverse(L,print);//依次输出表L中的元素 DestroyList(L);//销毁表L printf("销毁L后,L=%u\n",L); }程序运行结果: