问题背景:据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。
示例代码:
头文件(list.h)
#pragma once
#ifndef LIST_H
#define LIST_H
#include<stdlib.h>
typedef int ElemType;
typedef struct List_ {
ElemType data;
struct List_* next;
}LNode,*List;
int Locate(List L, int i);
#endif
源文件(list.c)
#include<string.h>
#include<stdio.h>
#include"list.h"
int main() {
LNode G;
int i;
printf("请输入表中元素序号的最大值:");
scanf_s("%d", &i);
Locate(&G, i);
}
int Locate(List L, int i) {
L = (List)malloc(sizeof(LNode));
L->next = L;
int j,w,k=0,d=0;
List q=L;
for (j = 1;j <= i;j++) {
List p;
p = (List)malloc(sizeof(LNode));
p->data = j;
q->next = p;
p->next = L;
q = p;
}
List r,x;
r = L;
while (d < i-2) {
x = r->next;
if(x!=L&&x->data!=0){
k++;//记录次数,看其是否是3的倍数
}
if (k % 3 == 0&&k!=w) {
d++;
x->data = 0;
}
r = x;
w = k;//由于后面空结点较多,k在循环几次后很可能不会改变,故启用另一个变量记录k在上一个循环的值,看两个是否相等;
}
while (i > 0) {
if (L->data > 0) {
printf("%d\n", L->data);
}
L = L->next;
i--;
}
return 0;
}
写该程序中应注意的问题:
代码思路:首先创建一个链表,并根据总人数依次创建结点,每个结点按顺序给予结点的数据域(从1开始);在“自杀”部分,把死去的人的数据域设为0;
链表创建部分比较简单,重要的是后面的每报一个“3”就杀死那个人,这里采用一个循环(while(d<i-2)),i是总人数,d是要杀死的总人数;
此处注意:(1)遇到头结点时,k这个计数器不能+1;
(2)由于后续会有很多data域等于0,所以要注意循环时上一次跟这一次k的计数不能相同(因为如果相同的话,k可能连续几次循环都是3的倍数,从而导致循环提前退出);
(3)为了解决(2)的问题,可声明另一个变量w记录下前一次的报数k,然后在下一次循环中判断k是否等于w;
最后,输出不会被杀的位置序号:采用了while循环,以i(链表里元素总个数)大于0为循环条件,依次将data域大于0的数字输出。如下图所示: