详解约瑟夫环问题及其相关的C语言算法实现
什么是约瑟夫环问题?
约瑟夫环问题是一个著名的数学问题,也称作是约瑟夫问题。一般来说,问题描述为:有 $n$ 个人围成一圈,从第 $k$ 个人开始报数,每报到第 $m$ 个人,就将该人从圈中杀死,然后从杀死该人的下一个人开始重新报数,直到圈中只剩下一个人为止。求圆圈中最后一个剩下的人的编号。
该问题有多种解法,其中比较常见的是使用链表模拟整个过程,或者是使用递归。
下面是 C 语言下的一种常见实现:
int josephus(int n, int k, int m) {
int i, j;
Node *t = head, *p = NULL;
t->val = 1;
for (i = 2; i <= n; ++i) {
t = (t->next = (Node *) malloc(sizeof(Node)));
t->val = i;
}
t->next = head;
p = t;
for (i = 0; i < k - 1; i++)
p = p->next;
while (t != t->next) {
for (i = 0; i < m - 1; ++i) {
p = t;
t = t->next;
}
p->next = t->next;
free(t);
t = p->next;
}
return t->val;
}
算法实现说明
算法思路
我们可以用链表来模拟这个环,首先按顺序生成一个从1到n的链表,则环的最后一个节点的值就是最后的胜者。由于每次数到 m 时对应的节点要出链表,因此每一轮中数到 m 的节点找到并出链就转化为了当前节点向后走 m-1 步。
数据结构定义
我们需要定义一个简单的链表结构,在 C 语言中,可以使用 struct
来定义。
struct _Node {
int val;
struct _Node *next;
};
typedef struct _Node Node;
算法实现
- 定义链表头和尾,以及链表中每个节点的值 $val$。
- 循环生成节点,将其中 $val$ 的值依次赋值为 1~n,同时将结尾节点的
next
指向头部。 - 循环找到数到 $k$ 的节点,并标记该节点的 $val$ 为 $1$。
- 循环数到 $m$ 的节点,将当前节点 $t$ 在环中去掉,并将指向该节点的上一个节点 $p$ 的
next
指向 $t$ 的下一个节点,并释放掉 $t$ 节点的空间。 - 最终链表中只剩下一个节点,该节点的 $val$ 即为最后的胜者。
示例:
假设有 $n=5$ 个人围成一圈($1,2,3,4,5$):
- 规定从第 $k=3$ 个人开始报数
- 每次数到第 $m=2$ 个人就将当前该位置的人出圈,即被杀死
- 求出在约瑟夫环问题中最后存活下来的人的编号
我们可以使用以下 C 代码进行模拟实现:
#include <stdio.h>
#include <stdlib.h>
struct _Node {
int val;
struct _Node *next;
};
typedef struct _Node Node;
int josephus(int n, int k, int m) {
int i, j;
Node *t = (Node *) malloc(sizeof(Node));
Node *head = t, *p = NULL;
t->val = 1;
for (i = 2; i <= n; ++i) {
t = (t->next = (Node *) malloc(sizeof(Node)));
t->val = i;
}
t->next = head;
p = t;
for (i = 0; i < k - 1; i++)
p = p->next;
while (t != t->next) {
for (i = 0; i < m - 1; ++i) {
p = t;
t = t->next;
}
p->next = t->next;
free(t);
t = p->next;
}
return t->val;
}
int main() {
int n, k, m;
printf("n = ");
scanf("%d", &n);
printf("k = ");
scanf("%d", &k);
printf("m = ");
scanf("%d", &m);
printf("被删掉的编号为:%d\n", josephus(n, k, m));
return 0;
}
运行结果:
n = 5
k = 3
m = 2
被删掉的编号为:4
再假定有 $n=7$ 个人围成一圈($0,1,2,3,4,5,6$),从第 $k=2$ 个人开始报数,每次数到第 $m=3$ 个人就将当前该位置的人出圈,即被杀死,求出在约瑟夫环问题中最后存活下来的人的编号。可以在以下代码的基础上进行修改:
#include <stdio.h>
#include <stdlib.h>
struct _Node {
int val;
struct _Node *next;
};
typedef struct _Node Node;
int josephus(int n, int k, int m) {
int i, j;
Node *t = (Node *) malloc(sizeof(Node));
Node *head = t, *p = NULL;
t->val = 0;
for (i = 1; i <= n; ++i) {
t = (t->next = (Node *) malloc(sizeof(Node)));
t->val = i;
}
t->next = head;
p = t;
for (i = 0; i < k - 1; i++)
p = p->next;
while (t != t->next) {
for (i = 0; i < m - 1; ++i) {
p = t;
t = t->next;
}
p->next = t->next;
free(t);
t = p->next;
}
return t->val;
}
int main() {
int n, k, m;
printf("n = ");
scanf("%d", &n);
printf("k = ");
scanf("%d", &k);
printf("m = ");
scanf("%d", &m);
printf("被删掉的编号为:%d\n", josephus(n, k, m));
return 0;
}
运行结果:
n = 7
k = 2
m = 3
被删掉的编号为:6
这就是约瑟夫环问题的详解及相关C语言的算法实现。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解约瑟夫环问题及其相关的C语言算法实现 - Python技术站