详解约瑟夫环问题及其相关的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++进行Cocos2d-x游戏开发入门过程中的要点解析

    使用C++进行Cocos2d-x游戏开发入门过程中的要点解析 1. 环境搭建 在C++进行Cocos2d-x游戏开发之前,需要先搭建好开发环境。搭建环境的步骤主要包括以下几个步骤: 安装Cocos2d-x:在官网下载Cocos2d-x最新版本,并安装配置好环境变量。 安装开发工具:根据个人喜好选择一个适合自己的开发工具,比如Visual Studio或者Xc…

    C 2023年5月24日
    00
  • 详解C 语言项目中.h文件和.c文件的关系

    关于“详解C语言项目中.h文件和.c文件的关系”的完整攻略,我可以为你提供以下详细说明: 一、H文件和C文件的定义 在C语言项目中,通常会使用.h文件和.c文件来定义函数、类型、变量和宏等,具体来说: .h 文件,也称为头文件(Header File),是一种包含函数、变量、常量、结构体、宏等声明的文件,用于在多个源文件中共享同一组声明。在一个H文件中,通常…

    C 2023年5月23日
    00
  • C++智能指针模板应用详细介绍

    C++智能指针模板应用详细介绍 智能指针的概念 在C++中,当我们使用new创建了一个对象时,需要手动的调用delete来释放内存。但是,如果在某个地方忘记释放内存,就会导致内存泄漏问题。为了避免这个问题,我们可以使用智能指针来管理内存。 一个智能指针是一个类,它行为像一个指针,但它还额外提供了内存管理的功能。智能指针类会通过在构造函数中调用new和在析构函…

    C 2023年5月22日
    00
  • C++实现简单的通讯录管理系统

    下面我来详细讲解“C++实现简单的通讯录管理系统”的完整攻略。 系统概述 通讯录管理系统是一个简单的信息管理系统。该系统可以实现以下功能: 添加联系人 显示联系人 删除联系人 查找联系人 修改联系人 清空联系人 退出通讯录管理系统 系统实现过程 设计流程 分析需求,确定功能模块 绘制流程图,确定各模块的处理流程 完成代码实现 运行测试 编写代码 首先,我们需…

    C 2023年5月23日
    00
  • C/C++高精度运算(大整数运算)详细讲解

    C/C++高精度运算(大整数运算)详细讲解 简介 在进行高精度运算时,我们需要使用到很大的整数进行计算,如:1000的阶乘,1到1000的和等。而C/C++默认的整型数据类型一般只能存储到2^32-1或2^64-1这样的范围,需要我们使用数组或链表等结构来存储这类大数。本篇文章将详细介绍如何使用C/C++实现大整数和高精度运算。 实现方式 在C/C++中,大…

    C 2023年5月22日
    00
  • C语言实现的猴子偷桃之类算法

    C语言实现的猴子偷桃之类算法 算法思路 猴子偷桃是一个经典的算法问题,其思路如下: 有一堆桃子,猴子第一天吃掉一半,发现还不过瘾,就又吃了一个;第二天又吃掉剩下的一半,发现还不过瘾,又吃了一个;以后每天都这样吃,直到最后只剩一个桃子为止。求原来有多少桃子。 为了方便解题,我们可以反向思考,即从最后一天向前推断。假设在第N天时只剩下一个桃子,那么在第N-1天时…

    C 2023年5月22日
    00
  • C语言选择排序算法及实例代码

    C语言选择排序算法及实例代码 算法介绍 选择排序算法是一种简单的排序算法,它的基本思想是依次遍历数组元素,每次找到剩余元素中的最小值,将其放到未排序部分的最前面。它的时间复杂度为O(n²),空间复杂度为O(1),适用于各种数据规模。 选择排序算法的流程如下: 在未排序序列中找到最小元素,存放到排序序列的起始位置 再从剩余未排序元素中继续寻找最小元素,然后放到…

    C 2023年5月30日
    00
  • C语言编写简单的定时关机程序

    当需要在计算机操作完一部分后定时自动关机时,我们可以通过编写简单的定时关机程序实现此功能。C语言是一种高效、安全的编程语言,可以用来编写此类程序。下面是关于如何编写简单的定时关机程序的攻略: 步骤1:导入头文件和主函数 在编写程序时,需要使用一些头文件和主函数。以下是需要使用的头文件和主函数命令的示例代码: #include <stdlib.h>…

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