約瑟夫環問題(C語言、數據結構版)
一、問題描述
N個人圍城一桌(首位相連),約定從1報數,報到數為k的人出局,然后下一位又從1開始報,以此類推。最后留下的人獲勝。(有很多類似問題,如猴子選代王等等,解法都一樣)
二、思路分析
(1)可將人的順序簡單編號,從1到N;
(2)構造一個循環鏈表,可以解決首位相連的問題,同時如果將人的編號改為人名或者其他比較方便
(3)將人的編號插入到結構體的Data域;
(4)遍歷人的編號,輸出參與的人的編號;
(5)開始報數,從頭報數,報到k的人出局(刪除次結點),(輸出出局的人更人性化)避免浪費,可釋放次結點。直到人數只有一個人時,退出循環。輸出獲勝的人。
(6)注意:在寫刪除刪除結點的函數時都是針對K>=2的情況處理,所以要考慮k=1的情況,要是出局的密碼為1時則最后一個獲勝。
三、算法實現
(1)構造結構體
typedef struct Node{ int Num;//Data域 struct Node *next; }JoseNode, *PNode, *HNode;//PNode為指針變量,HNode為頭結點
(2)得到頭結點
HNode h = ((HNode)malloc(sizeof(JoseNode)));
(3)初始化循環單鏈表
int JoseInit(HNode *h) { if (!h) { printf("初始化鏈表錯誤!\n");return 0; } (*h)->next = (*h);//循環單鏈表 return 1; }
(4)插入算法
int JoseInsert(JoseNode *h, int pos, int x) { PNode p=h,q; int i=1;if (pos == 1)/*尾插法*/ { p->Num = x; p->next = p; return 1; } while(i<pos-1) { p=p->next; i++; } q=(PNode)malloc(sizeof(JoseNode)); q->Num=x; q->next=p->next; p->next=q; return 1; }
for (i = 1; i <=N; i++) { JoseInsert(h, i, i); }
(4)遍歷算法
void TraverseList(HNode h, int M)
{
int i = 0;
PNode p = h;
printf("參與的人的編號為:\n");
while (i<M)
{
printf("%d\t", p->Num);
p = p->next;
i++;
}
printf("\n");
}
(5)找到出局的人,並刪除(刪除算法)
if(k > 1)//考慮出局密碼為1時的情況
JoseDelete(h, N, k);
else
{
for(i = 1; i < N; i++)
printf("出局的人為:%d號\n",i);
printf("***************獲勝者為:%d號***************",N);
}
int JoseDelete(HNode h, int M, int k)
{ int i;
PNode p=h,q;
while(M>1)//循環終止條件,只剩一個人時
{
for(i=1;i<k-1;i++)//此處為i<k-1,因為要找到的是出局的人的前一個結點①
{
p=p->next;
}
q=p->next;
p->next=q->next;
printf("出局的人為:%d號\n",q->Num);
free(q);//釋放次結點
p=p->next;//與①處的相對應,①處找到的只是出局的人的前一個結點
M--;
}
printf("***************獲勝者為:%d號***************",p->Num);
return 1;
}
四、完整代碼
1 #include <stdio.h> 2 #include <malloc.h> 3 4 /*構建結構體*/ 5 typedef struct Node{ 6 int Num; 7 struct Node *next; 8 }JoseNode, *PNode, *HNode; 9 10 /**********初始化循環單鏈表*********/ 11 int JoseInit(HNode *h) 12 { 13 if (!h) 14 { 15 printf("初始化鏈表錯誤!\n"); 16 return 0; 17 } 18 (*h)->next = (*h);//循環單鏈表 19 return 1; 20 21 } 22 23 /*************單鏈表插入操作**********/ 24 int JoseInsert(JoseNode *h, int pos, int x) 25 { 26 PNode p=h,q; 27 int i=1; 28 if (pos == 1)/*尾插法*/ 29 { 30 p->Num = x; 31 p->next = p; 32 return 1; 33 } 34 while(i<pos-1) 35 { 36 p=p->next; 37 i++; 38 } 39 q=(PNode)malloc(sizeof(JoseNode)); 40 q->Num=x; 41 q->next=p->next; 42 p->next=q; 43 return 1; 44 } 45 46 /*遍歷*/ 47 void TraverseList(HNode h, int M) 48 { 49 int i = 0; 50 PNode p = h; 51 printf("參與的人的編號為:\n"); 52 while (i<M) 53 { 54 printf("%d\t", p->Num); 55 p = p->next; 56 i++; 57 } 58 printf("\n"); 59 } 60 /**************出局函數****************/ 61 62 int JoseDelete(HNode h, int M, int k) 63 { int i; 64 PNode p=h,q; 65 while(M>1) 66 { 67 for(i=1;i<k-1;i++) 68 { 69 p=p->next; 70 } 71 72 q=p->next; 73 p->next=q->next; 74 printf("出局的人為:%d號\n",q->Num); 75 free(q); 76 77 p=p->next; 78 M--; 79 } 80 printf("***************獲勝者為:%d號***************",p->Num); 81 return 1; 82 } 83 84 85 /***************************************/ 86 int main() 87 { 88 int i;//計數器 89 int N;//參與的人數 90 int k;//報數密碼 91 printf("請輸入參與人數:"); 92 scanf("%d",&N); 93 printf("請輸入出局密碼:"); 94 scanf("%d",&k); 95 96 /**************得到頭結點****************/ 97 HNode h = ((HNode)malloc(sizeof(JoseNode))); 98 99 /***************初始化單鏈表************/ 100 JoseInit(&h); 101 102 /******將編號插入到循環單鏈表中******/ 103 for (i = 1; i <=N; i++) 104 { 105 JoseInsert(h, i, i); 106 } 107 /**************遍歷單鏈表***************/ 108 TraverseList(h,N); 109 110 /***************出局函數************/ 111 if(k > 1) 112 JoseDelete(h, N, k); 113 else 114 { 115 for(i = 1; i < N; i++) 116 printf("出局的人為:%d號\n",i); 117 printf("***************獲勝者為:%d號***************",N); 118 } 119 120 printf("\n"); 121 printf("\n"); 122 return 0; 123 }
五、實驗驗證

經過驗證,實驗成功!如果有不足的地方,請大家指出來,一起探討!寫得不清楚的可以與我聯系。
