约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)
如有错误,欢迎指正。
问题阐述
m个人围成一圈,按顺序编号。从第一个人开始从1到m报数,凡报到m的人退出圈子,求解最后留下的人的初始编号。程序运行示例;
6 3(两个输入数据之间有空格)
输入格式:scanf("%d%d", &n,&m);
输出格式:printf ("%d", loop[ dest1]):
解决思路
1.根据输入的人数创建环形链表(结构体存储每个人对应的编号)
2.利用变量count来记录报数的次数,当报到m时,将对应的节点从环形链表里面去除
3.重复步骤2,直到链表只剩下一个人的时候,输出结果
解决方法(2中方法)
方法一(注重下一个节点的报数)
此方法基于这个游戏的m不可能为1,且刚结束的一轮,下一轮第一个不会被踢出局。
注意事项:1.在创建环形链表的关键,先当作单链表创建,在于当链表走到最后一个节点时,它的next要指向链表的第一个,这就形成了环形链表(可以看作先制造一个绳子,当达到要求后,再把它首位链接形成环形)
2.在结果判断时,变量count代表的意思下一个人报数的次数,如一开始count=1,表示还没开始时,xian是head(头)节点,count代表的是第一个节点为第一次轮报数的1号,当开始进行循环,count=2,此时xian是第一个节点,count代表下一个节点即第二个节点为第一次轮报数的2号。也就是说,count代表的是当前节点的下一个节点应该报的数。
3.当指向下一个节点为游戏要求的m时
此时让count=1,即表示下一个节点重新为第一个节点。
4.当count代表的下一个节点报的数为m时,进行 3. 当中的操作,将其踢出局。
#include<stdio.h>
#include<malloc.h>
typedef struct student
{
int num;
struct student* next;
}S;
void Input(S** head,int n)//根据输入,创建相应的链表
{
S* qian, * xian;
int count = 0;
qian = (S *)malloc(sizeof(S));
* head = qian;
qian->next = NULL;
do
{
xian = (S*)malloc(sizeof(S));
xian->num = count + 1;
if (count != n - 1)//当节点不是最后一个时,当作单链表创建
{
xian->next = NULL;
}
else//当节点是最后一个时,将节点的next重新指向链表的开头
{
xian->next = (*head)->next;
}
qian->next = xian;
qian = xian;
count++;
} while (count < n);
}
void Result(S* head,int m)
{
int count = 1;
S* xian;
for (xian = head->next; xian != xian->next; xian = xian->next)
//xian节点从环形链表第一个开始,当xian节点的下一个也是自己时,代表只剩下自己。不然就继续进行游戏(下一个人报数)
{
count++;
if (count == m)
{
xian->next = xian->next->next;//具体细节看注意事项 3.
count = 1;
}
}
printf("%d",xian->num);//最后打印结果(最后剩下的人)
}
int main()
{
int n, m;
S* head;
printf("请输入m,n: ");
scanf("%d %d",&n,&m);
Input(&head,n);
Result(head,m);
return 0;
}
方法二(注重当前节点的报数)
注意事项:1.在创建环形链表的关键,先当作单链表创建,在于当链表走到最后一个节点时,它的next要指向链表的第一个,这就形成了环形链表(可以看作先制造一个绳子,当达到要求后,再把它首位链接形成环形)
2.在结果判断时,变量count代表是xian节点当前的报数,如还没开始是,count=0,表示xian节点报数为0号,当开始游戏时,count=1,代表xian节点的报号数,如第一次的count=1,xian节点为第一个节点,表示的是第一个节点在第一轮的报数为1号,当count=2时,xian节点为第二个节点,此时count表示第二个节点报数为2号。
3.当报数号为m时
此时让count=0,表示新一轮游戏开始。
4.当count代表的节点报的数为m时,进行 3. 当中的操作,将其踢出局。
#include<stdio.h>
#include<malloc.h>
typedef struct student
{
int num;
struct student * next;
}S;
void Input(S** head, int n)//创建环形链表,上一个方法有细讲,不做阐述
{
S* qian, * xian;
int count = 0;
qian = (S*)malloc(sizeof(S));
*head = qian;
qian->next = NULL;
do
{
xian = (S*)malloc(sizeof(S));
xian->num = count + 1;
if (count != n - 1)
{
xian->next = NULL;
}
else
{
xian->next = (*head)->next;
}
qian->next = xian;
qian = xian;
count++;
} while (count < n);
}
void Result(S* head, int n, int m)
{
int count = 0;//当前节点的报数号
int people = n;//用于记录当前人数
S* qian, * xian;
qian = head;//最开始的qian节点
xian = head->next;//环形链表的开始
while (people != 1)//当人数不为1时,继续游戏
{
count++;//当前节点报号数
if (count != m)//当报号数不为m时,正常进行履历链表
{
qian = xian;
xian = xian->next;
}
else//当报号数为m时,通过注意事项 3. 将当前节点踢出局
{
qian->next = xian->next;
xian = xian->next;
count = 0;
people--;//人数减1
}
}
printf("%d", xian->num);//输出结果
}
int main()
{
int n, m;
int i = 0;
S* head;
printf("请输入m,n: ");
scanf("%d %d", &n, &m);
Input(&head, n);
Result(head, n, m);
return 0;
}