详解约瑟夫环问题及其相关的C语言算法实现

详解约瑟夫环问题及其相关的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;

算法实现

  1. 定义链表头和尾,以及链表中每个节点的值 $val$。
  2. 循环生成节点,将其中 $val$ 的值依次赋值为 1~n,同时将结尾节点的 next 指向头部。
  3. 循环找到数到 $k$ 的节点,并标记该节点的 $val$ 为 $1$。
  4. 循环数到 $m$ 的节点,将当前节点 $t$ 在环中去掉,并将指向该节点的上一个节点 $p$ 的 next 指向 $t$ 的下一个节点,并释放掉 $t$ 节点的空间。
  5. 最终链表中只剩下一个节点,该节点的 $val$ 即为最后的胜者。

示例:

假设有 $n=5$ 个人围成一圈($1,2,3,4,5$):

  1. 规定从第 $k=3$ 个人开始报数
  2. 每次数到第 $m=2$ 个人就将当前该位置的人出圈,即被杀死
  3. 求出在约瑟夫环问题中最后存活下来的人的编号

我们可以使用以下 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技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C 程序 查找字符的 ASCII 值

    为了查找字符的ASCII值,我们可以使用C程序来完成。下面是使用攻略: 准备工作 在开始使用C语言编写程序之前,需要先安装一些开发环境,包括GCC编译器,以及一个代码编辑器,比如Visual Studio Code等。 步骤如下: 输入需要查找ASCII值的字符 首先,我们需要通过键盘输入需要查找ASCII值的字符,使用C语言中的字符变量来存储输入的字符。比…

    C 2023年5月9日
    00
  • C语言入门的一些基本资源推荐和程序语法概览

    C语言入门资源推荐和程序语法概览 C语言是一门重要的编程语言,在计算机科学和软件开发中得到广泛应用。如果你想要学习C语言,以下是一些资源推荐和程序语法概览,可以帮助你顺利入门。 入门资源推荐 1. 教材 学习一门新语言,选择一本好的教材非常重要。以下几本教材对于初学者尤其有用: 《C Primer Plus》(第6版):经典C语言入门教材,详尽全面的学习内容…

    C 2023年5月22日
    00
  • 你想知道的do{…}while(0)的作用,都在这里了

    0、引言                 我们在嵌入式开发的过程中,经常可以碰到在一些宏定义或者是代码段中使用了do {…} while(0)的语句,从语义上理解,do {…} while(0)内的逻辑就只执行一次,并没有循环执行,粗略看来,似乎画蛇添足了,那么为什么还需要在只执行一次的逻辑外面加上一层do {…} while(0)语句呢?实际上…

    C语言 2023年4月18日
    00
  • Java详细讲解异常Exception的处理

    Java详细讲解异常Exception的处理 什么是异常Exception 异常(Exception)指的是程序运行过程中不正常(错误)的情况,例如输入输出错误、计算错误、网络连接中断等情况。一般来说,出现异常会导致程序停止运行。 在Java中,异常被抛出后可以被程序处理,以免程序崩溃。Java中的异常分为两种类型:受检异常(Checked Exceptio…

    C 2023年5月22日
    00
  • 贪吃蛇游戏C++命令行版实例代码

    我们来详细讲解“贪吃蛇游戏C++命令行版实例代码”的完整攻略。 1. 程序结构 在开始编写代码前,我们需要先了解程序的结构。程序需要实现以下功能: 初始化游戏地图。 生成蛇,并初始化蛇头、蛇身方向等信息。 随机生成食物。 判断蛇是否撞到了边界或者自身,以及是否吃到了食物。 更新蛇的位置。 更新游戏地图并在命令行中显示。 基于上述功能,我们可以将程序结构设计为…

    C 2023年5月24日
    00
  • C/C++ 中extern关键字详解

    C/C++ 中extern关键字详解 在 C/C++ 中,extern 是一个很常见的关键字,常用于声明全局变量或函数。本文将对 extern 关键字进行详细讲解。 1. 变量声明 当在多个源文件中引用同一全局变量时,需要在其中一个源文件中定义该全局变量,然后在其它源文件中使用 extern 关键字声明该变量。这样确保了在多文件编译时,每个文件都将引用同一变…

    C 2023年5月23日
    00
  • C语言实现职工工资管理系统的示例代码

    下面是对于“C语言实现职工工资管理系统的示例代码”的完整攻略,包含了过程、示例说明以及代码实现: 1. 需求分析 该工资管理系统主要包括以下功能: 录入职工信息 查询职工信息 删除职工信息 修改职工信息 计算职工工资 根据上述需求,我们可以将职工信息抽象为一个结构体,包括工号、姓名、性别、年龄、基本工资等成员变量。通过调用各种函数实现各项功能,并将所有信息存…

    C 2023年5月23日
    00
  • C语言指针预定义类型

    C语言中,为了让指针类型更加易于使用和理解,已经预定义了几种指针类型。下面是它们的名称和描述: void *:指向任意类型的指针。 char *:指向字符类型的指针。 int *:指向整型的指针。 float *:指向单精度浮点类型的指针。 double *:指向双精度浮点类型的指针。 使用这些预定义的指针类型,可以更快地定义和使用指针类型变量,而不必手动指…

    C 2023年5月9日
    00
合作推广
合作推广
分享本页
返回顶部