问题描述
约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈.每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
代码(C语言)
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define SUCCESS 1
#define NUMBER 7 //总人数
typedef int Status;
typedef struct Joseph
{
int password; //正整数密码
int index;
struct Joseph *next; //后继
} LNode, *LinkList;
Status CreatList_T(LinkList *L, int n);
Status PrintIndex(LinkList *L, int n);
Status ListDel(LinkList *L, int i);
Status ListDel_Elem(LinkList *L, LinkList pos);
int main(void)
{
LinkList L;
CreatList_T(&L, NUMBER); //建立总人数为NUMBER的链表
PrintIndex(&L, NUMBER); //按照约瑟夫环规则输出出列顺序
return 0;
}
/* 尾插法建立无头结点的链表,同时为每个人输入其密码 */
Status CreatList_T(LinkList *L, int n)
{
*L = (LinkList)malloc(sizeof(LNode));
printf("Please input the password of 1st people: "); //先为第一个人赋值
scanf("%d", &((*L)->password));
(*L)->index = 1;
(*L)->next = *L;
LinkList tail = *L;
for (int i = 0; i < n - 1; i++) //为其余人赋值
{
LinkList s = (LinkList)malloc(sizeof(LNode));
printf("Please input the password of %dth people: ", i + 2);
scanf("%d", &s->password);
s->index = i + 2;
s->next = *L; //尾元指向首元
tail->next = s;
tail = s; //tail指向尾元
}
return SUCCESS;
}
/* 删除链表中的第i个元素 */
Status ListDel(LinkList *L, int i)
{
if (*L == NULL)
return ERROR;
LinkList p = *L, q;
int j = 1;
if (i == 1) //区分首节点与其余节点的删除方式
{
while (p->next != *L)
p = p->next;
*L = (*L)->next;
free(p->next);
p->next = *L;
}
else
{
while (j < i - 1) //将p指向pos的前驱
{
p = p->next, j++;
}
if (p->next != *L) //当前删除节点不是首元节点
{
q = p->next; //先用q指向当前删除元素
p->next = p->next->next; //再将p后移
free(q);
}
else //当前删除节点是首元结点
{
*L = (*L)->next; //将L指向第二个结点
free(p->next);
p->next = *L;
}
}
return SUCCESS;
}
/* 将链表中的元素pos删除 */
Status ListDel_Elem(LinkList *L, LinkList pos)
{
if (*L == NULL)
return ERROR;
LinkList p = *L, q;
if (pos == *L) //区分首节点与其余节点的删除方式
{
while (p->next != *L) //将p指向尾元
p = p->next;
*L = (*L)->next;
free(p->next);
p->next = *L;
}
else
{
while (p->next != pos) //将p指向pos的前驱
p = p->next;
q = p->next; //先用q指向当前删除元素
p->next = p->next->next; //再将p后移
free(q);
}
return SUCCESS;
}
/* 按照约瑟夫环规则输出出列顺序 */
Status PrintIndex(LinkList *L, int n)
{
int limit;
LinkList p = *L, q;
printf("Please input the limit: ");
scanf("%d", &limit); //要求用户输入上限值
printf("The order in which they listed was:\n\t");
for (int i = 0; i < n; i++)
{
if (limit < 1) //上限必须为正整数
return ERROR;
for (int j = 1; j < limit; j++)
//找到下一位出列者,注意初始已经移动了一位
p = p->next;
if(p->next != p)
printf("%d -> ", p->index);
else
printf("%d", p->index);
limit = p->password;
q = p;
p = p->next; //指向下一位
ListDel_Elem(L, q); //删除链表中当前出列的人
}
return SUCCESS;
}