C语言循环单链表实现约瑟夫环(Josephus)问题
本文介绍如何使用C语言和循环单链表解决经典的约瑟夫环问题。通过构建环形数据结构,模拟n个人围坐一圈并按特定规则淘汰的过程,提供完整的源码逻辑与实现细节。
约瑟夫环问题描述
约瑟夫环(Josephus Problem)是一个著名的数学应用题。假设有 n 个人围坐在一个圆桌周围,从第 s 个人开始报数,数到第 m 的那个人出列;他的下一位又从 1 开始报数,数到第 m 的人又出列,依此规律重复下去,直到圆周围的人全部出列。本方案的目标是设计一个程序,确定这 n 个人的出列次序序列。
算法设计思路
由于参与者是围成一圈运动的,使用具有“回环”特性的结构最为合适,因此选择循环单链表作为核心存储结构。
- 节点定义:每个节点包含编号(
number)和指向下一个节点的指针(pNext)。 - 链表构建:将
n个节点依次连接,最后将尾节点的pNext指向头节点,形成闭环。 - 位置定位:链表的起始头指针默认指向第一个节点。要找到从第
s个人开始的位置,需要遍历链表找到对应节点。 - 淘汰逻辑:
- 进行
n轮循环,每轮淘汰一人。 - 在每一轮中,移动指针
m-1次,此时当前指针指向的就是待淘汰者。 - 输出淘汰者的编号,并将其标记为已出列(如设为0),防止后续再次被选中。
- 调整指针至下一个有效节点,准备下一轮报数。
C语言源代码实现
以下代码实现了上述逻辑。程序首先创建循环单链表,然后根据用户输入的 n、s、m 三个变量计算并打印出列顺序。注意,代码中使用了 malloc 动态分配内存。
#include <stdio.h>
#include <malloc.h>
typedef struct Node
{
int number;
struct Node * pNext;
}NODE, *PNODE;
// 函数声明
PNODE creat_list(int n);
int main (void)
{
int n, m, s;
printf("约瑟夫环问题:\n");
printf("设有n(编号为 0 1 2 ... n-1)个人围坐一圈,现从第s个人开始报数,数到m的人出列,\n求最后的出列顺序。\n");
printf("请设置n, s, m :\n");
printf("n = ");
scanf("%d", &n);
printf("s = ");
scanf("%d", &s);
printf("m = ");
scanf("%d", &m); // 获取问题参数
PNODE pHead = NULL;
pHead = creat_list(n);
if (pHead == NULL) return 0;
pHead->number = 1; // 初始化头节点编号,确保闭环正确性
PNODE p = pHead; // 工作指针
int i, j, k;
// 第一步:移动指针到起始位置(第s个人)
for(j = 1; j < s; j++)
{
p = p->pNext;
}
// 第二步:逐个淘汰,直到所有人都出列
for(i = 1; i <= n; i++)
{
// 内部循环:寻找第m个待出列的人
for(k = 1; k < m; )
{
// 检查下一个节点是否已经出列
if(p -> pNext -> number != 0)
{
p = p -> pNext;
k++;
}
else
{
// 如果下一个节点已出列,跳过该节点继续找下一个有效的
p = p -> pNext;
}
}
// 输出当前淘汰者的编号
printf("%d ", p -> number);
// 标记为已出列
p -> number = 0;
// 寻找下一个还未出列的节点,准备下一轮
do
{
p = p -> pNext;
}while(p -> number == 0);
}
printf("\n");
return 0;
}
// 创建循环单链表
PNODE creat_list(int n)
{
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if (!pHead) return NULL;
PNODE pTail = pHead;
int i;
// 生成其余节点并链接成环
for(i = 2; i <= n; i++)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (!pNew) break;
pNew->number = i;
pTail->pNext = pNew;
pNew->pNext = pHead; // 关键:构成循环
pTail = pNew;
}
return pHead;
}
代码逻辑关键点解析
- 闭环构建:
creat_list函数中,新节点创建后立即将其pNext指向头节点pHead,这是实现循环操作的基础。 - 跳过出列者:在
for(k = 1; k < m;)循环中,判断条件p -> pNext -> number != 0确保了如果下一个人已经出局,指针会继续向后移,直到找到一个状态正常的节点才算作一次有效的计数。 - 更新起点:每次淘汰后,代码通过
do-while循环直接跳过所有已标记为0的节点,保证下一轮报数是从最近的活人开始的。
总结
通过循环单链表处理约瑟夫环问题,能够直观地反映物理世界中“围圈”的特性。该方法的时间复杂度取决于具体的实现细节,但通过合理的指针移动逻辑,可以高效地模拟出局过程。以上就是C语言循环单链表实现约瑟夫环问题的详细内容,更多关于C语言数据结构算法的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













