关于循环链表就是将尾结点的next不是指向NULL而是重新指向头结点或者首节点,这样整个链表就形成了一个圆环,无论从哪一个节点都可以顺利地遍历链表的每个元素,而单链表却只能从头结点才可以做到。
下面我们可以用一个经典案例体验下循环链表的妙处。那就是著名的约瑟夫问题。
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
上述约瑟夫环可以作为一个循环链表,将1-n分别放进一个个节点中,然后每出列一个就删除一个节点,并且打印出节点数据。
#include<stdio.h> #include<stdlib.h> #include<string.h> struct NODE{ int data; struct NODE* next; }; struct NODE* Creatlink(int n); void DeleteNode(struct NODE* head,int man,int num); int main(void){ int man,n,num; printf("请输入环内人数:"); while(1){ scanf("%d",&n); getchar(); if(n<1){ printf("error!请从新输入"); }else{ break; } } printf("请输入从第几个人开始:"); while(1){ scanf("%d",&man); getchar(); if(man<1||man>n){ printf("error!请从新输入"); }else{ break; } } printf("请输入报数从1到几:"); while(1){ scanf("%d",&num); getchar(); if(num<1){ printf("error!请从新输入"); }else{ break; } } struct NODE* head =Creatlink(n); printf("出列顺序表:\n"); DeleteNode(head,man,num); return 0; } struct NODE* Creatlink(int n){ int len,i,val; struct NODE* head=malloc(sizeof*head); struct NODE* move=head; move->next=NULL; if(head==NULL){ printf("分配内存失败"); exit(-1); } len=n; for(i=1;i<=len;i++){ struct NODE* fresh=malloc(sizeof*fresh); if(fresh==NULL){ printf("分配内存失败"); exit(-1); } fresh->data=i; move->next=fresh; fresh->next=NULL; move=fresh; } return head; } void DeleteNode(struct NODE* head,int man,int num){ int m; struct NODE* save; struct NODE* move=head->next; for(int i=1;i<man;i++){ move=move->next; } while(move->next!=move){ for(int i=1;i<num-1;i++){ move=move->next; } save=move->next; printf("%d ",save->data); move->next=move->next->next; move=move->next; free(save); save=NULL; } printf("%d \n",move->data); free(move); move=NULL; free(head); head=NULL; return; }对应单链表的就有双向链表,前者只有后继元素的地址,而后者则还有前驱元素的地址,这样虽然增加了一些内存上的开支,但是也带来了很多功能和效率上的好处。比如更加有利于搜索和查找元素,而且双向链表的增加和删除操作也更加方便,安全,不会一不小心将整个链表丢失掉。
任然使用上述循环链表中的案例,将约瑟夫环问题用双向链表实现一遍。
#include<stdio.h> #include<stdlib.h> #include<string.h> struct NODE{ int data; struct NODE* next; struct NODE* prior; }; struct NODE* Creatlink(int n); void DeleteNode(struct NODE* head,int man,int num); int main(void){ int man,n,num; printf("请输入环内人数:"); while(1){ scanf("%d",&n); getchar(); if(n<1){ printf("error!请从新输入"); }else{ break; } } printf("请输入从第几个人开始:"); while(1){ scanf("%d",&man); getchar(); if(man<1||man>n){ printf("error!请从新输入"); }else{ break; } } printf("请输入报数从1到几:"); while(1){ scanf("%d",&num); getchar(); if(num<1){ printf("error!请从新输入"); }else{ break; } } struct NODE* head =Creatlink(n); printf("出列顺序表:\n"); DeleteNode(head,man,num); return 0; } struct NODE* Creatlink(int n){ int len,i,val; struct NODE* head=malloc(sizeof*head); struct NODE* move=head; move->next=NULL; if(head==NULL){ printf("分配内存失败"); exit(-1); } len=n; for(i=1;i<=len;i++){ struct NODE* fresh=malloc(sizeof*fresh); if(fresh==NULL){ printf("分配内存失败"); exit(-1); } fresh->data=i; move->next=fresh; fresh->next=NULL; fresh->prior=move; move=fresh; } move->next=head->next;//题目要求不包含空的头结点,所以指向首节点 head->next->prior=move; return head; } void DeleteNode(struct NODE* head,int man,int num){ int m; struct NODE* save; struct NODE* move=head->next; for(int i=1;i<man;i++){ move=move->next; } while(move->next!=move){ for(int i=1;i<num;i++){ move=move->next; } save=move; printf("%d ",save->data); move->next->prior=move->prior; move->prior->next=move->next; move=move->next; free(save); save=NULL; } printf("%d \n",move->data); free(move); move=NULL; free(head); head=NULL; return; }