/* 此解法有一bug,假如有8个人,从第一个人开始数,每说到1就删除,相当于自删除,程序崩溃 */
约瑟夫问题,有n个人,编号为1,2,...,n,围成一个圆圈,按照顺时针方向从编号为k的人从1开始报数,报数为m的人出列,如此重复下去,直到所有的人都出列。编写一个算法,要求输入n,k,m,按照出列的顺序输出编号。
主要借助了两个游标进行移动,p有两个意义,第一,报数结束,p指向被删除的结点,第二,删除结点之后,p又指向开始报数的第一人。q作为桥梁进行衔接,因为需要删除p指向的结点。两个游标配合完成任务!
/*----------------完整代码@映雪--------------*/ #include <iostream> using namespace std; typedef struct Node { int data; struct Node *next; }ListNode,*LinkList; /*函数声明*/ LinkList CreateCycList(int n);/*创建一个长度为n的循环单链表*/ void Josephus(LinkList head,int n,int m,int k); /*长度为n的循环单链表中,报数为编号为m的出列*/ int main() { LinkList h; int n,k,m; printf("输入环中人的个数n="); scanf("%d",&n); printf("输入开始报数的序号k="); scanf("%d",&k); printf("报数为m的人出列m="); scanf("%d",&m); h=CreateCycList(n); Josephus(h,n,m,k); return 0; } /*-----------------算法核心段-----------------*/ void Josephus(LinkList head,int n,int m,int k) { ListNode *p,*q; int i; p=head; for(i=1;i<k;i++)/*从第k个人开始报数,将p先移到这个位置,准备开始*/ { q=p; p=p->next; } while(p->next!=p)/*开始报数....*/ { for(i=1;i<m;i++)/*数到m的人出列*/ { q=p; p=p->next; } q->next=p->next;/*q作为辅助[衔接]p左右的两个结点,等价于删除p*/ printf("%4d",p->data); free(p);/*释放结点p*/ p=q->next; /*p指向下一个结点,重新开始报数*/ } printf("%4d ",p->data);/*只剩最后一个结点!*/ } /*----------------建立循环链表------------------*/ LinkList CreateCycList(int n) /*建立链表,用两个游标s,r配合完成!*/ { LinkList head=NULL; ListNode *s,*r; int i; for(i=1;i<=n;i++) { s=(ListNode*)malloc(sizeof(ListNode)); s->data=i; s->next=NULL; if(head==NULL) head=s; else r->next=s; r=s; } r->next=head; return head; }