# 约瑟夫环问题重述 Problem Details

n个人围成一圈，从第一个人开始报数，第m个出局，游戏从下一个人继续，直到最后一人存活，游戏结束。试问每个人的出局顺序。

# 创建循环链表 Create a Circular List

## C

typedef struct node
{
int number;
struct node *next;
}person;


person *CirList( int n )
{
head->number = 1;	//The 1st person is 1.

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->next;
}
BodyCreator->next = head;	//After all the person nodes are created, attach the BodyCreator to the head to make a circle
}


head->number = 1;


2.指针BodyCreator用于指向待插入新结点的结点位置，看似累赘，实则起到了尾指针的作用，相对的body结点则是较为独立的新结点，将其插入到BodyCreator后成功入链表。

3.离开计数循环后，将BodyCreator的后继指向头指针至关重要，这保证了“循环”二字：

BodyCreator->next = head;


## C++

C++是面向对象的语言，类的使用是C++的特质，因此直接定义两个类，结点类CirListNode和循环链表类CirList：

class CirListNode
{
public:
CirListNode()
{
next = 0;
}
CirListNode( int el, CirListNode *ptr = 0 )
{
info = el;
next = ptr;
}

int info;
CirListNode *next;

}

class CirList()
{
public:
CirList();
~CirList();
int isEmpty()
{
}
void addToTail(int);		//Use it to form the CirList
int deleteFromTail();
void deleteNode(int);
void Traverse();			//Traverse the CirList
void JosephOut(int);		//Core Function
private:
CirListNode *tail;
}


1. 循环链表的构造函数：
CirList::CirList()
{
head->info = 1;				//Same as C version
tail = head;				//Must give a value to tail
tail->next = head;			//Make a circle
}


1. 循环链表的析构函数：
CirList::~CirList()
{
}

1. 利用尾插法完成构建循环链表，相对于在C语言中使用BodyCreator的麻烦，CirList 中的私有tail尾指针很好地充当了标记插入位置的功能：
CirList::AddToTail(int el)
{
if(isEmpty())
{
tail = new CirListNode(el);
tail = tail->next;
}
else
{
tail->next = new CirListNode(el, tail->next);	//Create a new node first, then attach it to the positon after tail
tail = tail->next;	//Move tail to the next node
}
}

1. 遍历整个循环链表并输出，只遍历一次到尾指针为止：
CirList::Traverse()
{
cout << tmp->info << "  ";
tmp = tmp->next;
{
cout << tmp->info << "  ";
tmp = tmp->next;
}
}


# 核心解法 Core Solution

## Joseph Ring in C

void JosephOuto(person *head, int start, int out)
{
while(p->number != start)
{
tail = p;		//Keypoint,don't forget it
p = p->next;	//Move p to the start number
}
while(p->next != p)
{
for( int i = 0; i < out; i++)
{
tail = p;	//Have to highlight again
p = p->next;
}
tail->next = p->next;	//delete *p
printf("The person who is out is: %d .\n", p->number);
free(p);
p = tail->next;
}
printf("The last person who is out is: %d .\n", p-> number);
free(p);
}


## Joseph Ring in C++

CirList::JosephOut(int v)
{
CirListNode *out = tail;
{
for (int i = 1; i < v; i++)	//Every time one person is out, flag just move (v-1) times
{
flag = flag->next;
out = out->next;
}
out->next = flag->next;
cout << "The person who is out is: " << flag->info << endl;
free(flag);
flag = out->next；
}
cout << "The last person who is out is: " << flag->info << endl;
free(flag);
free(out);
}


# 总结 Conclusions

CSDN博客上的大神相当多，敝人参考过诸多前例，例如几行就解决的递归思想的代码，也有巧妙设计数组解决的。相信这些方法也能在作者的抛砖引玉下显得更为精巧和有趣。也希望各位能够轻松读懂这篇文章的内容~

# 写在最后

• 3
点赞
• 0
评论
• 19
收藏
• 打赏
• 扫一扫，分享海报

11-24

05-02 1万+
07-01 1366
03-29 1479
09-29 2540
09-24 2694
11-18 3387
03-11
06-03 5770
12-04
03-18 1576
12-25
05-08 5700

¥2 ¥4 ¥6 ¥10 ¥20

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。