问题 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;
}