/* 此解法有一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\n",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; }