问题描述:
约瑟夫问题的一种描述是:编号为1,2,3~n的n个人按顺时针方向围坐一圈,没人持有一个密码(正整数),开始时任选一个整数作为报数上限值m,
从第一个人开始顺时针自1开始数序报数,报道m时停止报数。报m的人出列,将他的密码作为新的m值,从他顺时针的方向上下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。设计一个程序,求出出列顺序。
利用单向循环列表作为存储结构模拟过程,按照出列顺序打印出个人的编号。
例如:m的初始值为20,n=7,7个人的密码分别是3,1,7,2,4,8,4,出列的顺序为6,1,4,7,2,3,5.
解题思路:
1.利用m=m%n,进行求余计算,然后遍历到此节点的前一个结点,进行删除操作
2.每次输出后更改m和n的值
3.结束的条件
#include<iostream>
using namespace std;
struct ListNode//创建节点
{
int number;//number代表每个人的序列号
int password;//每个人的密码
struct ListNode* next;
};
struct ListNode* readList(int n)//创建链表,传入参数n时确定链表的长度
{
struct ListNode* head = NULL, * last = NULL, * p;
int temp;
int a = n;
for (int i = 1; i <= a; i++)
{
scanf_s("%d", &temp);//temp代表每个人的密码
p = (struct ListNode*)malloc(sizeof(struct ListNode));
p->number = i;
p->password = temp;
p->next = NULL;
if (head == NULL)
head = p;
else last->next = p;
last = p;
}
last->next = head;//尾部结点指向头节点,构成单向循环链表
return head;
}
int main()
{
int m, n;
struct ListNode* L;
printf("请输入m和n的值");
scanf_s("%d %d",&m,&n);
printf( "请依次输入每个人的密码:");
L = readList(n);
while (n)//结束条件
{
struct ListNode* temp;
m = m % n;
for (int i = 1; i < m - 1; i++)//找到需要删除节点的前一个节点
{
L = L->next;
}
temp = L->next;
L->next = temp->next;
printf("%d", temp->number);
n--;
m = temp->password;
if (n != 0)//条件
{
if (m % n != 1)
{
L = L->next;
}
}
free(temp);
}
return 0;
}