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;
}