问题 C: Josephus问题(Ⅰ)
题目描述
n个人排成一圈,按顺时针方向依次编号1,2,3…n。从编号为1的人开始顺时针"一二"报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。最终一定会剩下一个人。试问最后剩下的人的编号。
要求程序模拟题意来实现,使用不带头结点的循环链表。本题只需要提交两个函数CreateList和 Execute的实现, 请注意提交语言使用C++。
/*********************************/
/*CreateList创建一个从1到n的不带头结点的循环链表,返回指向结点1的头指针*/
LinkList CreateList(int n);
/*Execute从链表中删除一个人。
*@h 不带头结点的单循环链表
*@k,h指向的结点为第1个结点,删除第k个结点(含循环绕回的情况),k保证大于1.
*Execute返回第k+1个结点的指针
*/
LinkList Execute(LinkList h, int k);
/*********************************/为方便同学们测试,下面提供除两个函数外的其他代码。
#include<iostream>
using namespace std;typedef struct LNode
{int data;struct LNode *next;
} LNode, *LinkList;int main()
{int n;while(cin >> n){LinkList h;h = CreateList(n);while(--n)h = Execute(h, 2);cout << h->data << endl;delete h;}return 0;
}
输入
不超过1000组数据。
每组数据一行,每行一个正整数,代表人数n。 (1 <= n <= 1000)
输出
每组输入数据输出一行, 仅包含一个整数,代表最后剩下的人的编号。
样例输入 Copy
7
2
样例输出 Copy
7
1
提示
第一组数据出队顺序: 2 4 6 1 5 3
解题步骤
模拟题意
每次报数为2的人出局,即出局的人的后一位为重新开头
示例输入7的运算逻辑如下
下面是对代码的详细解析:
-
头文件和命名空间:
- 包含
<iostream>
头文件,用于输入输出操作。 - 使用
using namespace std;
来避免在标准库类型和函数前加std::
。
- 包含
-
链表节点结构体定义:
- 定义了一个结构体
LNode
,包含一个int data
成员变量和一个指向同类型结构体的指针next
。这个结构体用于表示链表中的节点。
- 定义了一个结构体
-
类型别名定义:
- 使用
typedef
创建了LNode, *LinkList
的别名,方便后续代码中使用。
- 使用
-
创建链表函数
CreateList
:- 接受一个整数参数
n
,表示链表中节点的数量。 - 动态分配一个新节点作为链表的起始节点,并初始化其数据为 1。
- 使用循环动态分配更多的节点,并按顺序设置它们的数据,从 2 到 n。
- 将链表的最后一个节点的
next
指针指向链表的起始节点,形成循环。 - 返回指向链表起始节点的指针。
- 接受一个整数参数
-
执行删除操作函数
Execute
:- 接受一个链表头指针
h
和一个整数k
,表示要删除的节点的序号(从 1 开始)。 - 通过循环找到第
k-1
个节点,即要删除节点的前一个节点。 - 将前一个节点的
next
指针指向要删除节点的下一个节点,从而删除了第k
个节点。 - 更新链表头指针
h
为删除操作后的下一个节点。 - 释放要删除节点的内存。
- 返回更新后的链表头指针。
- 接受一个链表头指针
-
主函数
main
:- 从标准输入读取整数
n
,直到输入结束。 - 使用
CreateList
函数创建一个长度为n
的循环链表。 - 使用循环调用
Execute
函数删除第 2 个节点,直到只剩下一个节点。 - 输出剩余节点的数据。
- 释放链表头节点的内存。
- 从标准输入读取整数
-
程序结束:
- 返回 0,表示程序正常结束。
代码逻辑分析:
- 这段代码模拟了一个约瑟夫环问题(Josephus problem),其中
n
表示初始人数,每次删除第 2 个节点,直到只剩下一个节点。 CreateList
函数创建了一个循环链表,Execute
函数实现了删除指定节点的操作。- 主函数中的循环模拟了约瑟夫环问题的迭代过程。
改进建议:
- 移除
Execute
函数中的h=x->next;
这行代码,因为它是多余的。 - 在
main
函数中,可以在删除操作后更新h
的值,例如使用h = Execute(h, 2);
。 - 考虑增加对输入有效性的检查,确保
n
是一个合理的正整数。
代码实现
这里因为小编的链表部分学的不是很好,所以找朋友指导了一下
代码如下(创建循环链表):
/*创建一个从1到n的不带头结点的循环链表,返回指向结点1的头指针*/
LinkList CreateList(int n){LinkList ans=new LNode;LinkList sum=ans;sum->data=1;for(int i=2;i<n+1;i++){sum->next=new LNode;sum=sum->next;sum->data=i;}sum->next=ans;return ans;
}//创建题目运算过程中所需要的循环链表,用以存储答案所存在的数字区间
代码如下(存储数据):
/*Execute从链表中删除一个人。
h为不带头结点的单循环链表,h指向的结点为第1个结点,
删除第k个结点(含循环绕回的情况),k保证大于1,Execute返回第k+1个结点的指针*/
LinkList Execute(LinkList h, int k){LinkList sum=h;for(int j=1;j<k-1;j++){sum=sum->next; }//不用分配新内存,直接使用原定义所用链表内存LinkList x=sum->next;sum->next=x->next;h=x->next;delete x;return h;
}
//在所存储的循环链表中查找答案所在正确位置并将其位置固定为h节点
完整代码
#include<iostream>
using namespace std;typedef struct LNode
{int data; //编号 struct LNode *next;
}LNode, *LinkList;/*创建一个从1到n的不带头结点的循环链表,返回指向结点1的头指针*/
LinkList CreateList(int n){LinkList ans=new LNode;LinkList sum=ans;sum->data=1;for(int i=2;i<n+1;i++){sum->next=new LNode;sum=sum->next;sum->data=i;}sum->next=ans;return ans;
}/*Execute从链表中删除一个人。
h为不带头结点的单循环链表,h指向的结点为第1个结点,
删除第k个结点(含循环绕回的情况),k保证大于1,Execute返回第k+1个结点的指针*/
LinkList Execute(LinkList h, int k){LinkList sum=h;for(int j=1;j<k-1;j++){sum=sum->next; }//不用分配新内存,直接使用原定义所用链表内存LinkList x=sum->next;sum->next=x->next;h=x->next;delete x;return h;
}int main()
{int n;while(cin >> n){LinkList h;h = CreateList(n);while(--n)h = Execute(h,2);cout << h->data << endl;delete h;}return 0;
}