设有n个人围坐在圆桌周围,现从第s个人开始报数,数到第m个人出列,然后从出列的下一个人重新开始报数,数到第m个人又出列...如此重复,直到所有人出列为止,出列结构即为约瑟夫问题结果。
约瑟夫问题是一个非常经典的题目,解决问题的方法有很多,我在这里分享一下我想出的两种方法。
首先是顺序表,我们可以将所有人用数组元素的下标来代表,元素的值为1表示这个人还在继续游戏,为0则表示已经出局。这样用C语言来描述游戏的情况就很轻松。当我们每一个要数m个人时,只需判断一下数过的每个元素的值,如果是1不用额外处理,如果是0就表示这个人已经出局,咱少数了,就再多数一次。
具体思路见代码:
void joseph(int n, int s, int m)
{
/*1.
创建一个大小为n的数组,以元素的下标来表示参与游戏的人,如people[0]表示第一个人的情况
其中,1表示这个人还在游戏中,0表示已经出列 */
int people[n] = {1};
for(int i=0; i<n; i++)
people[i] = 1;
int temp = 0; //计数,表示已出局的人数
/*2.
找到开始的那个人:第s个人*/
int p = s-1; //p用来表示当前某个人对应的下标
/*3.
开始一个大循环,直到所有人全部出局
从第s个开始数,数到第m个人,这表示我们实际上要数m-1次 */
while(temp < n)
{
for(int i=0; i<m-1; i++)
{
p = (p + 1) % n;
//每数一个人,下标右移一次,同时由于人群围成一圈,故到达末尾时需从0开始,即取余
if(people[p] == 0)
// 对于那些已经出局的人我们在计数时会数到,但实际上应该将他们去除
i--;
}
printf("%d\t", p+1); //输出结果
people[p] = 0; //将找到的那个人记为0,并将计数加1
temp++;
p = (p + 1) % n; //从出局的那个人后边的人继续
}
}
现在我们再来考虑一下用链表怎么做。
如果我们采用最简单的链表来做,那么实际上与顺序表没有什么区别,只是换了一种载体,甚至由于链表的特殊性,反而还更加麻烦了,因此我们采用单循环链表来做。单循环链表与普通的链表区别不大,唯一的区别就是,单循环链表最后一个节点的指针不是指向NULL,而是指向它的头部。
现在我们来看一下单循环链表的创建:
#include<stdlib.h>
//首先定义一个最简单的结构体,它由两个数据域data和指针next组成,
//其中data我们一会儿用来表示人的序号
typedef struct node
{
int data;
struct node *next;
}Lnode
Lnode *creat(int len)//尾插法创建单循环链表
{
Lnode *h, *p, *t;
int data;
h = (Lnode *)malloc(sizeof(Lnode));
h->next = h; //这里就是关键,next不再指向空,反而指向第一个节点
h->data = 1;
t = h;
for(int i=1; i<len; i++)
{
p = (Lnode *)malloc(sizeof(Lnode));
p->data = i+1;
p->next = h;
// 创建一个新的节点并赋值,由于我们是尾插法,该节点是最后一个,
//所以它的next指向第一个节点,如果右同学选择使用头插法,需要特别注意一下这一点
t->next = p;
t = p;
}
return h;
}
之后的步骤就很简单了,我们找到目标节点,将它的数据读出来,然后删除这个节点,之后重复上述动作就可以了。
此外还有一点,一个单独的链表节点是没办法删除的,因此我们还是需要一个计数的变量来判断是否结束游戏,不能直接以链表为空来作为结束的判断标志。
void joseph(int n, int s, int m)
{
Lnode *h = creat(n); //创建一个有n个值的无头结点的单循环链表
printf("已创建\n");
int temp = 0; //用于计数
Lnode *p;
p = h;
for(int i=0; i<s-1; i++) //使p指向第s个人
{
p = p->next;
}
while(true) //死循环走起
{
for(int i=0; i<m-2; i++) //使p指向第m个人的前一个
{
p = p->next;
}
printf("%d\n", p->next->data); //输出
p->next = p->next->next; //删除节点,右移指针
p = p->next;
temp++; //计数加一
if(temp == n) //判断游戏是否结束
break;
}
}
大体就是这样了,希望对大家有所帮助。