Josephu问题:据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人找到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 如何用循环链表来求解Josephu问题?
Josepy问题是一个经典的循环链表问题,今天我们就基于C语言使用循环链表来实现这一问题:
1、循环链表其实就是将单链表的最后一个成员的指针域指向链表表头所在的地址
代码实现如下:
1、先创建一个新的循环链表表头。
//创建链表
Josepy *Creat_Josepy()
{
Josepy *head = (Josepy *)malloc(sizeof(Josepy));
if(NULL == head){
printf("创建失败\n");
return NULL;
}
head->data = 1;//第一个成员
head->next = head;//循环链表
return head;
}
2、此处由于存储数据为连续的数据,所以我们选择头插法实现对链表的插入。
//头插
void Josepy_Insert_Head(Josepy *head, Data_t n)
{
Josepy *new = (Josepy *)malloc(sizeof(Josepy));
if(NULL == head){
printf("创建失败\n");
return;
}
new->data = n;
new->next = NULL;//新指针初始化
new->next = head->next;//新数据插入时先存储插入位置后的元素地址
head->next = new;//头指针指向新元素地址
}
3、此处计算链表长度,由于循环链表表头也存放数据,所以我们初始长度为1
int Josepy_Len(Josepy *head)
{
int len = 1;//由于此处将头作为第一个成员,所以链表长度(人数)最开始为1
Josepy *p = head->next;
while(p != head){
len++;
p = p->next;
}
return len;
}
4、此处为Josepy问题的具体实现
//josepy选择
void Josepy_Choose(Josepy *head, int k, int m)
{
Josepy *p = head;
Josepy *q = NULL;
int len = Josepy_Len(p);
int start = k-1;
while(start--){
p = p->next;
}
int num = m-2;//为了遍历到要删除成员的前一个成员
while(len != 0){
while(num--){
p = p->next;//要删除成员的前一个成员
q = p->next;//存放要删除成员的地址
}
printf("%d ", q->data);//打印要删除的成员
p->next = q->next;//要删除成员的上一个成员指向要删除成员的下一个成员
free(q);//删除
q = NULL;
num = m-1;
len--;
}
printf("\n");
return;
}
5、链表的打印
//打印链表
void Josepy_Print(Josepy *head)
{
Josepy *p = head->next;
printf("%d ", head->data);
while(p != head){
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
以上即为主要的程序段,下面我们来测试函数的功能实现:
从段错误中可见,我们将内部所有成员都删除了,并且其顺序都是符合条件的。
如有错误还望指正!!!