题目描述:
编号是1,2,……,n的n个人按照顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个仍开始顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他的顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。请设计一个程序输出出列顺序。
提示:存储结构采用不带头结点的循环单链表,结点结构如下:
typedef struct Node
{
int ID;
int password;
struct Node *next;
}LNode,*LinkList;
要求:
(1)编写建立循环单链表的函数,依次输入每个人的ID和password建立不带头结点的循环单链表。
(2)编写函数,按照规则依次删除相应的元素。
(3)main()函数调用(1)和(2)中的函数,输出约瑟夫序列。
注意:m=1 时需要特殊处理。
CreateList(LinkList L,int n)-创建不带头节点的循环链表
函数功能描述
接收空链表以及要接收数据的条数n,内部使用尾插法实现结点的插入,并在数据录入完成后实现链表的首位相连。
代码实现
//根据输入创建链表
LinkList CreateList(LinkList L,int n) {
L = (LinkList) malloc(sizeof(LNode));
LinkList nd; //新节点,接收数据,动态分配内存
LinkList p = L; //工作指针
scanf("%d %d",&L->ID,&L->password); //接收数据
L->next = NULL; //头指针指向空,代表空链表
while(n-- > 1) { //向链表内添加数据
nd = (LinkList) malloc(sizeof(LNode));
scanf("%d %d",&nd->ID,&nd->password); //接收数据
p->next = nd; //向链表后端添加节点
p = p->next;
if(n == 1) { //判断是否到达数据末尾
p->next = L; //连接首位,形成循环链表
} else {
p->next = NULL; //封尾链表
}
}
return L;
}
cycle(LinkList L,int m)-操作约瑟夫环并打印每次出圈人的序号
函数功能描述
接收目标链表以及报数值,运用递归思想实现约瑟夫问题
代码实现
int cycle(LinkList L,int m) {
int password;
if(L == L->next) { //判断环是否只剩下最后一个
printf("%d\n",L->ID);
return 0;
}
LinkList p = L; //工作指针
int num = 1; //记录迭代次数
if(m == 1) { //判断是否是m=1的特殊情况
password = L->password; //将表头元素的password取出用以递归调用并打印
printf("%d\n",L->ID);
while(p->next != L) { //迭代链表直至最后一个结点
p = p->next;
}
p->next = p->next->next; //删除表头结点,并将最后一个结点与第二个结点连接
p = p->next; //将工作指针移到删除结点的下一结点,以便递归调用
} else {
while(num++ < m-1) { //迭代至目标结点的上一结点,便于删除
p = p->next;
}
password = p->next->password; //将目标结点的password取出用以递归调用并打印
printf("%d\n",p->next->ID);
p->next = p->next->next; //删除目标结点并连接前后两结点
p = p->next; //将工作指针移到删除结点的下一结点,以便递归调用
}
cycle(p,password); //递归
}
main函数实现
int main(){
int n,m;
scanf("%d\n%d",&n,&m); //获取n,m值
LinkList N;
N = CreateList(N,n);
cycle(N,m);
return 0;
}
样例输入1
8
4
1 3
2 1
3 9
4 2
5 4
6 7
7 4
8 6
样例输出1
4
6
7
3
5
8
2
1
样例输入2
4
1
1 2
2 3
3 4
4 1
样例输出2
1
3
4
2