约瑟夫环问题介绍:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
# include <stdio.h> # include <stdlib.h> # include <assert.h> typedef int DataTp; typedef struct node { DataTp elem; struct node*pnext; }NODE; typedef NODE* LinkList; NODE* CreatList(int n); void MakeLoop(LinkList phead); void DeleteNode(LinkList phead,LinkList paim); int main (void) { int sum,num; //总人数 , 退出人的编号 int i; LinkList pstart; //头节点 LinkList paim; //待删除的目标节点的地址 LinkList pt; puts("输入总人数"); scanf("%d",&sum); puts("输入退出的编号"); scanf("%d",&num); pstart=CreatList(sum); //建立链表 MakeLoop(pstart); //变为循环链表 pt=pstart->pnext; //取出第一个数据节点地址 while(sum!=0) { for(i=0;i<num-1;i++) pt=pt->pnext; //往后寻址,找到退出目标的地址 paim=pt; //临时保存。确定删除节点的地址 pt=pt->pnext; //确定下次循环的头为pt printf("退出的是%d号 ",paim->elem); DeleteNode(pstart,paim); //删除 sum--; //总人数减少 } return 0; } NODE* CreatList(int n) { int i; NODE*p1,*p2; NODE * phead; phead=(NODE*)malloc(sizeof(NODE)); if(phead==NULL) { fputs("memory error! ",stderr); exit(EXIT_FAILURE); } phead->pnext=NULL; ///////////////////////////////// for(i=0;i<n;i++) { p1=(NODE*)malloc(sizeof(NODE)); //分配内存 if(p1==NULL) //检查合法性 { fputs("memory error! ",stderr); exit(EXIT_FAILURE); } p1->elem=i+1; //补全结点信息 p1->pnext=NULL; if(phead->pnext==NULL) //链接 phead->pnext=p1; else p2->pnext=p1; p2=p1; //重置 } return phead; } void MakeLoop(LinkList phead) { NODE*p=phead->pnext; //取出第一个数据节点 assert(p!=NULL); while(p->pnext!=NULL) p=p->pnext; p->pnext=phead->pnext; // 链接,构成循环链表 return ; } void DeleteNode(LinkList phead,LinkList paim) { NODE*p=phead->pnext; //取出第一个数据节点 assert(p!=NULL); while(p->pnext!=paim) p=p->pnext; p->pnext=p->pnext->pnext; if(paim==phead->pnext) //如果删除的是第一个数据节点, phead->pnext=p->pnext; //改变头节点指针域的值,使它始终指向 //循环链链表的某一个节点。而不丢失链表节点 free(paim); return ; }
链表初始状态:
刚开始写这个程序的时候,遇到一个Bug,那就是没有解决链表丢失问题。比如当退出的节点刚好为1号节点时
,1号节点就会被Free掉,这样就不能通过phead来访问各个节点了,整个链表就丢失了。
于是我在DeleteNode()函数里加了一个判断:当删除节点为第一个数据节点时,更改头节点的指针域的值,
使指向第一个数据节点的下一个节点,这样链表就不会丢失了。