首页 > 其他分享 >谈谈约瑟夫环的问题

谈谈约瑟夫环的问题

时间:2023-09-08 13:00:41浏览次数:35  
标签:链表 cur int 报数 next 问题 谈谈 约瑟夫 节点

约瑟夫环问题是一个经典的数学问题,名称来源于传说中的约瑟夫(Josephus)。这个问题描述了一个古老的故事,据说在罗马人占领乔塔帕特后,39个犹太人和约瑟夫及他的朋友们决定用自杀的方式来避免落入罗马人的手中。

他们站成一个圆圈,从一个人开始,每次由站在这个人后面的人报数,报数到第 m 个人,该人出圈;然后从出圈的下一个人开始,再由后面的人按同样的规则报数,直到所有人都出圈为止。约定所有人的编号为 0,1,2…n-1,从编号为 0 的人开始报数。

现在我们需要写一段代码来解决这个问题,输出最后剩下的那个人的编号。

首先,我们可以考虑使用暴力枚举的方法来解决该问题,即使用一个数组保存所有人的状态,每次循环中将编号为 m 的人标记为“已出圈”,最后查找状态为“未出圈”的人,并返回其编号。代码如下:

def josephus(n, m):
    people = [True] * n  # 初始化状态为全部在圈内
    count = 0  # 记录当前报数的人数
    index = 0  # 记录当前报数的人的编号

    while True:
        if people[index]:  # 如果这个人还在圈内
            count += 1  # 报数+1

        if count == m:  # 报数到达m,将这个人标记为“已出圈”
            people[index] = False
            count = 0  # 重置计数器

        index += 1  # 轮到下一个人报数
        if index == n:  # 如果已经报完一轮
            index = 0  # 圈回到编号为0的人

        if people.count(True) == 1:  # 如果只有一个人还在圈里,退出循环
            break

    return people.index(True)  # 返回最后一个在圈中的人的编号

上述代码中,我们使用一个布尔型数组 people 来记录每个人是否还在圈内。在每次循环中,我们先判断如果这个人还在圈内,就对计数器 count 进行加 1 的操作。如果计数器达到了报数的次数 m,就将这个人的状态标记为“已出圈”;同时,重置计数器。

接着,将下一个人作为报数的起点,一直循环到圈中只剩下一个人或者满足其他终止条件时结束循环。最后,返回最后一个在圈中的人的编号。

然而,时间复杂度为O(nm)的暴力枚举算法在 n和 m都很大的时候效率极低,因此我们需要考虑更优秀的解决方法。

其实,对于这个问题,早在19世纪就已经有了比较完美的解答。具体来说,当报数到达 m 时,我们可以直接将这个人从圈中删除,然后从下一个人重新开始报数。这样,不仅可以减少时间复杂度,也能够避免使用数组删除操作过程中的性能损失。

因此,我们可以考虑使用链表数据结构来模拟约瑟夫环的整个过程。具体来说,我们可以创建一个含有 m个节点的链表,每个节点表示一个人,并使用指针方式来表示不同的配置情况。

以下是使用 C++ 实现的代码:

#include <iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};

int josephus(int n, int m) {
    ListNode dummy(0);  // 创建一个哑节点,作为头结点的前驱节点

    ListNode* prev = &dummy;
    for (int i = 0; i < n; i++) {  // 创建包含n个节点的链表
        auto node = new ListNode(i);
        prev->next = node;
        prev = prev->next;
    }
    prev->next = dummy.next;  // 链接尾节点和头结点,形成一个环

    ListNode* cur = dummy.next;
    while (n > 1) {  // 当链表中还有至少两个节点时
        for (int i = 0; i < m - 2; i++) {  // 找到倒数第二个节点
            cur = cur->next;
        }

        auto temp = cur->next;  // 删除第m个节点
        cur->next = cur->next->next;
        delete temp;

        n--;
        cur = cur->next;  // 将当前节点指向下一个节点
    }

    return cur->val;  // 返回最后一个节点的值
}

int main() {
    int n = 5;
    int m = 3;

    int result = josephus(n, m);

    cout << "the last person's position is: " << result << endl;

    return 0;
}

在代码中,我们创建了一个含有 n个节点的链表,并将尾节点与头结点相连接,形成环形。在遍历过程中,每次找到第 n-1个节点,然后删除第 n个节点,并将当前节点指向下一个节点用于继续遍历。当链表中只剩下一个节点时,返回该节点的值即可。

使用示例:

int n = 5;
int m = 3;

int result = josephus(n, m);

cout << "the last person's position is: " << result << endl;

输出结果为:

the last person's position is: 3

以上是一篇关于约瑟夫环问题的博客,讲解了暴力枚举和链表两种实现方式。希望对您有所帮助!

标签:链表,cur,int,报数,next,问题,谈谈,约瑟夫,节点
From: https://blog.51cto.com/u_16182207/7408873

相关文章

  • 关于hive数据库添加信息到表中出现问题的原因细说
    问题来源在建表完成之后,尝试使用insertinto语句向表中添加数据信息,然后就一直不能成功,当然,添加的数据信息与表的字段类型是对应的;问题解决查阅相关资料发现,原来是虚拟机的内存不太够,然后就按照网上的建议,将下面的语句放置到hadoop下面的yarn-site.xml文件里面:<property>......
  • 解决npm ERR! Cannot read properties of null (reading ‘pickAlgorithm‘)报错问题
    转载自:https://www.cnblogs.com/zhyp/p/16920380.html=========解决方法:在终端中运行命令:npmcacheclear--force然后重新运行npmi命令,再次安装安装完成,没有出现报错npmrunserve运行项目,项目可以正常启动了。  安装vueCLI失败后,百度得知在终端执行命令:npmcleanc......
  • hashMap产生的循环依赖问题
    转:hashMap产生的循环依赖问题 这样就是一个很经典hashMap线程不安全导致的循环依赖,因为是个循环链表,就会导致数组一直重复扩容,导致集合的一个无限大,但是JDK1.8的时候,把头插法改成了尾插法,同时引进了红黑树,当连续扩容32次的时候会转换成红黑树,解决这个循环依赖的问题,但是还是......
  • 安装强化学习包gym报错问题及解决方法
    安装命令pipinstallgymnasium[all]如遇如下报错error:command'swig.exe'failed:Nosuchfileordirectory[endofoutput]note:Thiserrororiginatesfromasubprocess,andislikelynotaproblemwithpip.ERROR:Failedbuildingwheelfo......
  • Android全面屏下,默认不会全屏显示,屏幕底部会留黑问题
    公司以前的老项目,便出现了这种情况,网上搜索了各种资料,用了各种库,依然无法解决这个问题。如图所示:最终功夫不负有心人,在Application中看到了,这样一个属性android:resizeableActivity=“false”这个属性设置为了false,我们新建的项目,是没有这个属性的,然后我把这个属性设置为了true......
  • 谈谈JSF业务线程池的大小配置
    1.简介JSF业务线程池使用JDK的线程池技术,缺省情况下采用Cached模式(核心线程数20,最大线程数200)。此外,还提供了Fixed固定线程大小的模式,两种模式均可设置请求队列大小。本文旨在通过一个简化场景(“单服务应用”)下的负载测试,为“JSF业务线程池大小配置”提供基准测试结果,并形成一些......
  • 有意思的bug:Input在谷歌浏览器下会出现异常显示问题!
    ❝这篇文章要感谢抖音:程序员小山与BUG!你说是bug吧也算是bug,不是bug吧也不是bug,不影响使用,触发情况也不多,但万一测试提了这个bug还是要解决的[doge],在此记录一下这个有意思的bug!❞Bug说明谷歌浏览器是前置条件还要加上input输入框但输入框要有2个附加条件:type是number以及css的te......
  • 关于取模、进制的问题
    先来定义一下取模(b不等于0)。\(r(a,b)=a-b\times\lfloor\frac{a}{b}\rfloor=t\)下面讨论一下t的取值范围。b>0\(k=\lfloor\frac{a}{b}\rfloor\),则\(r(a,b)=a-kb\)。因为\(k\le\frac{a}{b}<k+1\),\(a-b\times\frac{a}{b}=0\)。配凑一下,就是\(a-bk-b&l......
  • 【问题记录】ApplicationContextAware 注入为空的问题
    1  前言今天在关于流程的群里发现有人问这个问题,简单来记录下哈,也就是Aware注入的时候为什么会为空呢?有的人说static的应该类名.进行等于,也有人说是类上的注解应该是@Component不应该是@Service,那我们来看看。2 剖析首先关于注解的@Service在这里可以理解为跟@C......
  • ELK日志缺失问题排查-多行日志聚合Logstash配置问题
    1.背景推荐系统的推荐请求追踪日志,通过ELK收集,方便遇到问题时,可以通过唯一标识sid来复现推荐过程最近在碰到了几个badcase,需要通过sid来查询推荐日志,但发现部分无法在kibana查询到2.分析推荐日志的整个收集流程如下:    线上机器日志平台FlumeKafkaLogstashE......