1.問題描述
約瑟夫環(約瑟夫問題)是一個數學的應用問題:已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。
鏈表思路:
我是通過單鏈表實現的,
首先要思考怎麼成一個圈?(循環鏈表不必考慮這個問題。)
1.給n個人依次從小到大編號,先將最大號碼放入鏈表,再定義一個指針plast代表表尾,此時它既爲表頭phead,又爲表尾。
2.依次將後面的號碼放入鏈表,表頭phead不斷更新,表尾plast始終指向最開始放入的最大號碼。
3.待號碼全部放入時,將表尾的next指向表頭,成環。
//先把最大的數放進去
SListPushFront(&phead, m);
plast = phead;
//依次放入剩下的數,形成鏈表
for (i = m - 1; i >=1; i--)
{
SListPushFront(&phead, i);
}
plast->next = phead;//形成一個環
然後再思考怎樣數數?
1.首先先找到數數的結束條件,就是只剩下一個人時,停止數數,玩家勝利。數數的次數應該=剩下的人數-1。每當有一個人出局時,數數次數就減少一次。
2.再找一個指針,它指向的節點的next就是要出局的節點。
cur = plast;//只有第一個跳的個數不一樣,這樣可以保證第一次的正確
for (; m > 1; m--)
{
for (i = 1; i < n; i++) //數數,跳n-1步後刪
{
cur = cur->next;
printf("%d號報%d\n", cur->data, i);
}
printf("%d號出圈\n", cur->next->data, i);
SListEraseAfter(cur);
}
測試
輸入人數m爲5,數的數字n爲2:
源代碼:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
//無頭單向非循環鏈表
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode *next;
}SListNode;
void SListInit(SListNode **pphead)//初始化
{
*pphead = NULL;
}
void SListPushFront(SListNode **pphead, SLTDataType x)//頭插
{
SListNode *tmp = BuySListNode(x);
tmp->next = (*pphead);
(*pphead) = tmp;
}
void SListEraseAfter(SListNode *pos)//刪除pos後面的節點,把pos的地址傳進來,改變pos指向的空間
{
SListNode *tmp = pos->next;
if (tmp == NULL)
{
return;
}
pos->next = tmp->next;
tmp = NULL;
}
int main()
{
int i = 0;
int n = 0;//數的數字
int m = 0;//數數的人數
SListNode* phead;
SListNode* plast=NULL;
SListNode* cur=NULL;
scanf("%d %d", &m, &n);
SListInit(&phead);
//先把最大的數放進去
SListPushFront(&phead, m);
plast = phead;
//依次放入剩下的數,形成鏈表
for (i = m - 1; i >=1; i--)
{
SListPushFront(&phead, i);
}
plast->next = phead;//形成一個環
cur = plast;//只有第一個跳的個數不一樣,這樣可以保證第一次的正確
for (; m > 1; m--)
{
for (i = 1; i < n; i++) //數數,跳n-1步後刪
{
cur = cur->next;
printf("%d號報%d\n", cur->data, i);
}
printf("%d號出圈\n", cur->next->data, i);
SListEraseAfter(cur);
}
printf("%d號勝利\n", cur->data);
free(cur);
system("pause");
return 0;
}