問題:n個人圍成一圈縫m報數,數到m的出列,後面的人開始繼續從1開始,數到m的出列,問最後剩下的一個人的序號是多少。
一.迴圈單鏈表求解
//迴圈隊列求解約瑟夫問題#include<stdio.h>#include<stdlib.h>typedef struct student{int data;struct student *next;}Node;void Delete(Node *p)//(1){Node *q;int j=1;q=p->next;p->next=q->next;free(q);}Node *Create(int n){Node *head=(Node *)malloc(sizeof(Node));Node *s, *r;r=head;for(int i=1; i<=n; i++){s=(Node *)malloc(sizeof(Node));s->data=i;r->next=s;r=s;}r->next=head->next;//(2)return head;}void Josephus(Node *head, int n, int m)//約瑟夫環主題函數{Node *p;p=head->next;int k=0;while(p!=p->next)//如果p==p->next了,說明只有一個節點了,目標達成。{k++;if(k==m-1)//(3){k=0;Delete(p);}p=p->next;}printf("\n%d is left\n",p->data);}int main(){Node *head;int n, m;printf("Input n and m\n");scanf("%d%d",&n,&m);//n是元素個數,m是到縫m報數,m必須大於head=Create(n);Josephus(head, n,m);system("pause");return 0;}注意://(1)這個函數與之前的隊列各種操作合集裡面的Del不一樣。那個個參數,一個頭結點一個待刪除節點的序號i。先找到第i-1個節點。然後刪除。這個不太一樣,僅僅是一個參數,而且是傳遞是某個指標。注意,由於單鏈表無法向前尋找,所以我傳遞的節點的指標過來之後,只能刪除這個節點的後面一個節點,所以無法刪除傳遞節點,只能刪除傳遞節點的後一個節點。所以要在後面的函數體裡面稍微處理一下。//(2)此處靈活處理了一下,還是保持帶前端節點的風範--頭結點head裡面不存值,head->next開始存值。但是考慮到在迴環的時候因為head裡面不存值,還得每次都判斷,不方便,就不把尾節點的next指向head了,而是尾節點的next直接指向head的next。這樣就不用每次都判斷了,反正head也沒啥用。//(3)這裡最關鍵,按照數組方法,這裡是k==m做判斷,因為k從開始,第一個節點的時候k=1,然後依次遞增。當增到m的時候,說明到了m個,開始報數,然後按理說改刪除這個節點,而此時,p也正好移動到了m號節點。但是由於Delete函數刪除的是所給節點的後續節點。所以此處判斷就要改為k==m-1,到m-1的時候,p也指向m-1的節點,這時把p傳給Delete就可以刪除第m節點了。
運行結果如下:
二.數組方式求解
//數組求解約瑟夫問題#include<stdio.h>#include<stdlib.h>#define nmax 150int main(){int i, k, out_cnt, n, m, num[nmax], *p;printf("please input n and m:\n");scanf("%d%d",&n, &m);p=num;for(i=0;i<n;i++){*(p+i)=i+1;}i=0;k=0;out_cnt=0;//out_cnt是用來計數出列個數的,一共出列n-1個,所以下面的while迴圈以out_cnt<n-1為邊界條件,out_num從0-n-2一共n-1個,出列了n-1也就是只剩個在列了while(out_cnt<n-1){if(*(p+i)!=0){k++;}if(k==m){*(p+i)=0;//鏈表方法中出列元素刪除節點,這裡無法刪除,採取置的方法。k=0;out_cnt++;//每縫k==m,出列一個,out_cnt++}i++;//每個迴圈結尾都有i++,好用來計算p[i],跟鏈表方法裡的p=p->next類似。if(i==n){i=0;}}while(*p==0)//跳過出列元素,找到剩下的那個{p++;}printf("%d is left\n",*p);system("pause");return 0;}