iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript三种方法解决约瑟夫环问题的方法
  • 655
分享到

JavaScript三种方法解决约瑟夫环问题的方法

2024-04-02 19:04:59 655人浏览 泡泡鱼
摘要

目录概述问题描述循环链表有序数组数学递归总结概述 约瑟夫环问题又称约瑟夫问题或丢手绢问题,是一道经典的算法问题。问题描述也有很多变式,但大体的解题思路是相同的。本篇将以循环链表、有序

概述

约瑟夫环问题又称约瑟夫问题或丢手绢问题,是一道经典的算法问题。问题描述也有很多变式,但大体的解题思路是相同的。本篇将以循环链表、有序数组、数学递归三种方式来解决约瑟夫环问题。

问题描述

先来看一下什么是约瑟夫环问题?

在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须,然后再由下一个重新报数,直到所有人都身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。

到了今天约瑟夫环问题的描述一般变成了:

在一间房中有N个人,所有人围成一圈顺时针开始报数,每次报到M的人退出游戏。退出游戏的下一个人再次重新开始报数,报到M的人再退出,依此循环,直到游戏只剩下最后一个人,则最后一个人的编号是多少?

循环链表

循环链表的解题思路比较简单,只需要将问题描述转换成代码即可。房间中的N个人组成一个长度为N的链表,首尾相接组成循环链表。列表中的每一项的值即为每个人的编号,在数到M时将该项(记为x)剔除,即该项的前一项的next不再指向x,而是x.next。依此规律遍历链表,直至链表中只剩下一项。

下面看一下代码实现。


function createList(num) {
    //链表节点的数据结构
    function createnode(value) {
        return {
            value: value,
            next: ''
        }
    }
    //链表头节点
    let head = createNode(1);
    let node = head;
    //自头节点之后创建节点之间的关联关系
    for (let i = 2; i <= num; i++) {
        node.next = createNode(i);
        node = node.next;
    }
    //最后一个节点指向头节点,构成循环链表
    node.next = head;
    return head;
}
function deleteListNode(num, nth) {
    //创建数据长度为num的循环链表
    let node = createList(num);
    //链表长度>1时,继续下一轮
    while (num > 1) {
        for (let i = 1; i <= nth - 1; i++) {
            if (i == nth - 1) {
                //i为nth-1,则node.next即为第nth个节点。剔除node.next
                node.next = node.next.next;
                //链表长度--
                num--;
            }
            node = node.next;
        }
    }
    //剩余的最后一个节点的value值即为最后一人编号
    return node.value
}
//deleteListNode(m,n)即可得到最终结果

有序数组

用有序数组来模拟循环链表,将数组第一次遍历剔除完成后剩余的数组项组成一个新的数组,再对新数组进行新一轮的遍历剔除,依此循环,直到数组长度为1。

下面看一下代码实现。


function jhonRing(num, nth) {
    let arr = [];
    //创建有序数组
    for (let i = 1; i <= num; i++) {
        arr[i - 1] = i;
    }
    //当数组长度大于nth时,数组不用循环遍历也可找到需要剔除的数据
    while (arr.length >= nth) {
        let newArr = [];
        //将数组末尾剩下的数据转移到新数组的前方,新一轮遍历从生于的数据开始
        let times = arr.length - arr.length % nth;
        newArr = arr.slice(times)
        arr.slice(0, times).map((item, index) => {
            //将除了剔除数据之外的其他数据加入新的数组
            if ((index + 1) % nth !== 0) {
                newArr.push(item)
            }
        })
        arr = newArr;
    }
    //当数组长度小于nth时,数组需要循环遍历才能找到要剔除的数据,通过取余操作可减少遍历的次数
    while (arr.length > 1) {
        //取余获取要剔除的数据的下标
        let index = nth % arr.length - 1;
        //剔除数据的后半部分与前半部分组成新的数组
        let newArr = arr.slice(index + 1).concat(arr.slice(0, index));
        arr = newArr;
    }
}
//jhonRing(num, nth)即可得到最终结果

用有序数组模拟链表的操作看上去跟链表差不多,时间复杂度看上去也一样,甚至代码也比不上链表简洁,但是为什么仍然要把这个方式提出来呢?这种方法的优势体现在M>>N的情况下,有序链表通过取余的方式有效的减少了循环遍历数组的次数。以N为3,M为100为例,链表需要循环遍历100/3+1次,而有序数组则根据取余操作的结果100/3-1=0,直接得到了要剔除的数据下标为0。

数学递归

用数学问题求解约瑟夫环问题时极易找不到一条有效的规律总结路线,下面以M=3举例讲述一下总结规律时走过的弯路。(可跳过无效的思路,直接阅读下方的有效思路)

比较多次数据量较小时的结果(❌)

N=1时,f(1,3)=1;
N=2时,f(2,3)=2;
N=3时,f(3,3)=2;
N=4时,f(4,3)=1;
N=5时,f(5,3)=4;
N=6时,f(6,3)=1;
N=7时,f(7,3)=4;
N=8时,f(8,3)=7;
N=9时,f(9,3)=1;
N=10时,f(10,3)=4;

通过举例发现,最终结果并不总是在某几个数之间,除了1,2,4以外还出现7,则之后的结果也有可能有类似的情况,即最终结果并不总是局限于某一个数之间。f(3,3) f(6,3) f(9,3)等N为M的倍数的情况并没有得到相同的结果,可见最终结果与3的倍数之间并不存在某种特殊联系。因此通过这种积累比较数据量较小时的结果来寻找规律的方案不合适。

比较前几次剔除数据之间的关系(❌)

//将N个人自1-N进行编号
1, 2, 3, 4........N-2,N-1, N
//第一次剔除的编号为M或M%N,可总结为M%N,记为K。则第二次剔除时的序列变为
K+1,K+2,.......N,1,2......K-1
//则第二次剔除的编号为K+M%(N-1) || M%(N-1)-(N-K-1)
依此得到规律
当N-(K+1)>M%(N-1)时,f(n)=f(n-1)+M%(N-(n-1))
当N-(K+1)<M%(N-1)时,f(n)=M%(N-(n-1))-(N-f(n-1)-1)

依此规律计算会发现,没有进行第二圈遍历时得到的结果是正确的,遍历进入第二圈之后的结果错误。根本原因在于进入第二圈之后的数据不再连续,第一圈遍历时剔除出的数据会影响第二圈遍历的结果,故此方案不合适。

自上而下分析问题,自下而上解决问题(✅)

用递归的思路去分析约瑟夫环问题。以N=10,M=3为例分析。

//将10个人从0开始编号(稍后解释为什么不能从1开始编号)
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
//将最后一个出列的人的编号记做f(10,3)
//在10个人编号后,第一个人出列后,得到新的队列及编号
3, 4, 5, 6, 7, 8, 9, 0, 1
//为了使新队列的编号连续,我们可以将新队列记做A,写作
3, 4, 5, 6, 7, 8, 9, 10%10, 11%10
//若将一个9人普通队列记做B,写作0,1,2,3,4,5,6,7,8的最终结果记做f(9,3),则新队列A的结果则可以表示为(f(9,3)+3)%10
//9人队列A是10人队列剔除一人后得到的,则9人队列按N=9,M=3,初始编号为3的规则进行游戏后得到的结果必然与N=10,M=3,初始编号为0的队列最后出列的人相同。
//故10人队列最后出列的人编号与9人队列A出列的人编号之间存在关系f(10,3)=(f(9,3)+3)%10

基于以上可以得到结论f(N,M)=(f(N-1,M)+M)%N。则最终编号的结果即为f(N,M)+1。
为什么编号不能从1开始呢?因为取余操作的返回结果是从0开始。


function Josephus(num,nth){
    if(num==1){
        return 0;
    }else{
        return (Josephus(num-1,nth)+nth)%num
    }
}
//Josephus(N,M)+1即为最终编号

对递归函数做一下优化


function JosephusR(num,nth){
    let value = 0;
    for(let i=1;i<=num;i++){
        //此处为对i取余,上述递归中num也是在逐渐变小的,所以此处为i而非num
        value=(value+nth)%i;
    }
    return value+1;
}
//JosephusR(N,M)即为最终编号

总结

通过数学递归方式的优化,将约瑟夫环问题的时间复杂度从最初的O(mn)降低到O(n)。通过循环链表的方式来解决问题确实是最快最容易想到的思路,但是这种数学递归的方式对解决此类算法问题来说更有思考的价值。

到此这篇关于javascript三种方法解决约瑟夫环问题的方法的文章就介绍到这了,更多相关JavaScript 约瑟夫环内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaScript三种方法解决约瑟夫环问题的方法

本文链接: https://www.lsjlt.com/news/135077.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript三种方法解决约瑟夫环问题的方法
    目录概述问题描述循环链表有序数组数学递归总结概述 约瑟夫环问题又称约瑟夫问题或丢手绢问题,是一道经典的算法问题。问题描述也有很多变式,但大体的解题思路是相同的。本篇将以循环链表、有序...
    99+
    2024-04-02
  • 详解基于C++实现约瑟夫环问题的三种解法
    目录一、前言二、循环链表模拟三、有序集合模拟四、递归公式解决五、结语一、前言 什么是约瑟夫环问题? 约瑟夫环问题在不同平台被"优化"描述的不一样,例如在牛客剑指offer叫孩子们的游...
    99+
    2024-04-02
  • PHP怎么解决约瑟夫环问题
    这篇文章主要讲解了“PHP怎么解决约瑟夫环问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP怎么解决约瑟夫环问题”吧!约瑟夫环问题(猴子选大王)PHP版约瑟夫斯问题问题有时候也被描述成...
    99+
    2023-06-22
  • 约瑟夫环的解法有哪些
    这篇文章主要介绍“约瑟夫环的解法有哪些”,在日常操作中,相信很多人在约瑟夫环的解法有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”约瑟夫环的解法有哪些”的疑惑有所帮助!接...
    99+
    2024-04-02
  • C/C++经典算法之约瑟夫问题详解
    目录什么是约瑟夫问题? 方法一:数组方法二:环形链表方法三:递归总结什么是约瑟夫问题?  约瑟夫问题:n个人围成一圈,初始编号从1~n排列,从约定编号为x的人开始...
    99+
    2024-04-02
  • Java用单向环形链表来解决约瑟夫环Josepfu问题
    简单介绍 如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链表,通俗讲就是让尾节点指向头结点。 单向环形链表应用场景:Josephu(约瑟...
    99+
    2024-04-02
  • C语言数据结构中约瑟夫环问题如何解决
    本文小编为大家详细介绍“C语言数据结构中约瑟夫环问题如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言数据结构中约瑟夫环问题如何解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。问题描述约瑟夫环问题的...
    99+
    2023-07-04
  • C/C++经典算法之约瑟夫问题的示例分析
    这篇文章给大家分享的是有关C/C++经典算法之约瑟夫问题的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是约瑟夫问题? 约瑟夫问题:n个人围成一圈,初始编号从1~n排列,从约定编号为x的人开...
    99+
    2023-06-20
  • Java数据结构与算法之双向链表、环形链表及约瑟夫问题深入理解
    目录一、双向链表二、环形链表及其应用:约瑟夫问题环形链表图示构建一个单向的环形链表思路遍历环形链表约瑟夫问题一、双向链表 使用带head头的双向链表实现 - 水浒英雄排行榜管理单向链...
    99+
    2024-04-02
  • C语言三种方法解决轮转数组问题
    目录题目1.题目描述2.要求3.原题链接二、相关知识点三、解决思路旋转法直接法空间换取时间题目 1.题目描述 给你一个数组,将数组中的元素向右轮转 k 个位置,其中&nbs...
    99+
    2024-04-02
  • Spring三种方法的注解自动注入问题
    目录Spring三种方法的注解自动注入1 @Autowired注解2 @Resource3 @InjectSpring 注解版 属性赋值 自动注入总结Spring三种方法的注解自动注...
    99+
    2022-12-28
    Spring注解 Spring自动注入 Spring注解自动注入
  • 三个方法解决php并发问题
    福利:[网络安全重磅福利:入门&进阶全套282G学习资源包免费分享 !] 解决php并发问题的方法有很多,具体可以使用MySQL的行级锁、乐观锁和Redis的分布式锁等技术来解决。此外,还可以使用消息队列、多进程、多线程等技术来解决php并...
    99+
    2023-08-19
    php 数据库 mysql 网络安全 PHP并发
  • JavaScript继承的三种方法实例
    继承 1. 什么是继承 继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承。 继承也是为了数据共...
    99+
    2024-04-02
  • 使用python求解迷宫问题的三种实现方法
    目录前言递归求解回溯求解队列求解总结前言 在迷宫问题中,给定入口和出口,要求找到路径。本文将讨论三种求解方法,递归求解、回溯求解和队列求解。 在介绍具体算法之前,先考虑将迷宫数字化。...
    99+
    2024-04-02
  • 解决在Mac上无法向U盘写入数据的问题:三种有效方法
    方法一:重新格式化U盘 Mac OS默认情况下不支持向NTFS卷写入。你可以选择重新格式化你的U盘为exFAT或者Mac OS扩展,下面是如何进行这个操作的步骤: 注意:格式化操作会删除U盘上的所有数据,所以在开始之前请务必备份你的数据。 ...
    99+
    2023-08-31
    macos java 开发语言
  • IE6 position:fixed问题的解决方法
    IE6 position:fixed问题的解决方法,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。...
    99+
    2024-04-02
  • 分享后端解决跨域问题的三种方案
    1.跨域的介绍 跨源资源共享(CORS——Cross-Origin Resource Sharing,跨源资源共享,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它源(域、协议或端口)...
    99+
    2023-09-01
    spring boot 网络 java Powered by 金山文档
  • Windows XP输入法不见了的三种解决方法
    Windows XP系统输入法不见了怎么办本文告诉恢复桌面右下角输入法小图标的方法。桌面任务栏右侧的输入法状态(也就是语言栏)不见了,通常有以下几种解决方法。windows XP系统输入法不见了怎么办恢复桌面右下角输入法...
    99+
    2023-05-25
    Windows XP 输入法 解决 输入 方法
  • 解决Pycharm模块安装慢问题的两种方法
    目录第一种 直接在Pycharm中修改第二种 修改pip.ini文件总结 pycharm 模块安装慢解决方法 接下来介绍的两种方法本质上都是改变镜像源 第一种 直接在Pyc...
    99+
    2022-12-22
    pycharm安装模块时速度很慢 pycharm模块安装 Pycharm模块安装慢
  • 全网多种方法解决com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure的问题
    文章目录 1. 复现错误2. 分析错误3. 解决问题4. 解决该错误的其他方法 1. 复现错误 今天在使用knife4j,调用后端接口时,报出如下错误: 于是,赶紧查看控制台的错误信息,错误信息如下所示: com.mysql...
    99+
    2023-08-16
    mysql java 数据库 后端 spring boot
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作