利用循环链表解决约瑟夫环问题(采用C及C++实现)
约瑟夫环问题重述 Problem Details
问题源于一个故事,讲述者据传为著名犹太历史学家Josephus。
罗马人占领乔塔帕特,39个犹太人外加Josephus和他的朋友共计41人躲进一个洞窟,为了躲避敌人的抓捕,决定集体自杀。自杀的规则是所有人围成一个圆圈,确定一个数字从第一个人开始报数,报到数的人自杀,从下一个人开始继续。然而Josephus和朋友反对,于是假装同意。通过将自己和朋友安排在第16和31的位置,避免了死亡的遭遇。
以上故事衍生成了一个经典的数学问题。法国数学家在《数目的游戏问题》提出了类似的问题。
综合各种形式,给出一种最常见的题目形式:
n个人围成一圈,从第一个人开始报数,第m个出局,游戏从下一个人继续,直到最后一人存活,游戏结束。试问每个人的出局顺序。
分析 Analysis
本题具有很多种解法,排除各种编程语言的差异,解法之间的主要区别是选用的数据结构不同。因此,本题分析的入手点即应当选择什么数据结构较优(或者说更顺手更易于理解)
本题可以采用的数据结构非常多,见诸于网络的有数组法、循环队列法、循环链表法等等,利用递归思想10行之内解决问题的大神也不少见。在这么多的方法中,分析题目可以很容易发现,所有的“人”,也就是各个数据单元,围成了“一圈”,进行循环报数。由此,关键词获得,圆,循环,首尾相接,很容易想到“循环队列”或者“循环链表”。循环链表与循环队列实质上是一种数据结构。这里我们尝试选用循环链表解决问题。
那么使用循环链表是否合适呢?
首先,相对于其它数据结构,其本来形式更直观地对应了题目的实际形象;
其次,循环链表可以很容易地构成“循环”;
最后,循环链表进行删除操作相对容易,更方便将其中一个结点删除而不破坏整个数据结构。
综上,使用循环链表是合适的。
创建循环链表 Create a Circular List
C
首先建立一个结点 node,同时因为本题对象只有 Josephus 环中的人,因此直接定义一个结点类型 person:
typedef struct node
{
int number;
struct node *next;
}person;
接下来就利用上面定义的 person 结点创建循环链表结构 CirList,int型变量n用于接收链表的结点数:
person *CirList( int n )
{
person *head = (person *)malloc(sizeof(person));//Create the head node
head->number = 1; //The 1st person is 1.
head->next = head; //Make head connects to itself making it the Circular List.
person *BodyCreator = head; //Set a pointer to help make the body
int i; //Every i is a person node coming after the head node
for (i = 2; i <= n; i++)
{
person *body = (person*)malloc(sizeof(person));
body->number = i; //Keypoint~Cause its the person after No.1 so he starts from 2
body->next = NULL;
body = BodyCreator->next; //Attach the body to the CirList
BodyCreator = BodyCreator