Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 380|回复: 0

使用java的循环单向链表解决约瑟夫问题

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-9 17:16:43 | 显示全部楼层 |阅读模式

    什么是约瑟夫问题

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

    约瑟夫的故事比较残忍,我们把约瑟夫问题改为一个小游戏,游戏规则如下:

      假设编号为1,2,3...n个人手拉手围坐在一圈,约定编号为k(1<= k <= n)的人开始报数,数到m的人出圈,他的下一位又从1开始报数,数到m的人又出圈,依次类推,直到剩余一个人即为胜出者,由此产生一个出圈编号的序列,

    使用循环链表模拟n个人围坐一圈

    首先我们定义一个循环链表,由此来模拟n个人围坐在一圈。下面先看下单链表的实现代码

     1 /**
     2  * 单向循环链表
     3  */
     4 class CycleSingleLinkedList{
     5     PersonalNode first = null;
     6     /**
     7      * 向循环链表中添加num个节点
     8      * @param num
     9      */
    10     public void add(int num){
    11         if(num < 1){
    12             System.out.printf("参数不合法");
    13             return;
    14         }
    15         PersonalNode curr = null;
    16         for (int i = 1; i <=num; i++) {
    17             PersonalNode personalNode = new PersonalNode(i);
    18             if(i == 1){
    19                 //添加第一个节点
    20                 first = personalNode;
    21                 curr = first;
    22                 curr.setNext(first);
    23             }else {
    24                 curr.setNext(personalNode);
    25                 personalNode.setNext(first);
    26                 curr = personalNode;
    27             }
    28         }
    29 
    30     }
    31 
    32 }
    33 
    34 /**
    35  * 人物节点
    36  */
    37 class PersonalNode {
    38     private int no;
    39     private PersonalNode next;
    40 
    41     public PersonalNode(int no){
    42         this.no = no;
    43     }
    44 
    45     public int getNo() {
    46         return no;
    47     }
    48 
    49     public void setNo(int no) {
    50         this.no = no;
    51     }
    52 
    53     public PersonalNode getNext() {
    54         return next;
    55     }
    56 
    57     public void setNext(PersonalNode next) {
    58         this.next = next;
    59     }
    60 }

    下面我们用图例的方式讲解一下添加n个节点的循环链表的流程,当创建第一个节点时,我们让first和current指向第一个节点,并将current的next指向first节点,以此来形成一个循环。当添加第二个节点时,我们将current的next指针指向第二个节点,第二个节点的next指向first,然后将current后移,即current指向第二个节点,以此类推。

     

    现在我们问题的第一步即n个人围坐在一起解决了,下面来实现第二步从第k个人开始数数。第二步比较简单,即让first指针后移k-1次,由此让first指针指向第一个数数的人。但由于first指向的节点需要出圈,这里我们再定义一个helper指针,来指向first指针的前一个节点,即让helper指针首先指向first,依此循环直到helper的next指针等于first(因为是循环链表)。并保持helper指针始终在first指针的前一个节点(只有一个节点时与first指针指向同一个节点)。假设k=2即从第二个人开始数,m=3即数到3的人出圈,如下图所示

     

    找到node4的人出圈,这时让helper的next等于first的next,然后让first等于helper的next,如下图所示

     

    继续从node1开始数,数到3,即node3出圈,此时指针指向情况如下图

    node3出圈后,下一个要出圈的为node1,依此循环,直到helper与first指针指向相同节点,即node2为最后一个要出圈的人。代码如下

     1 /**
     2      * 约瑟夫问题
     3      * @param n n个人围成圈
     4      * @param k 第k个人开始报数
     5      * @param m 数到m的人出圈
     6      */
     7     public void joseph(int n,int k,int m){
     8         //添加n个人
     9         add(n);
    10         //helper指针指向first的前一个节点
    11         PersonalNode helper = first;
    12         while (helper.getNext() != first){
    13             helper = helper.getNext();
    14         }
    15         //找到第k个人
    16         for (int i = 0; i < (k-1); i++) {
    17             first = first.getNext();
    18             helper = helper.getNext();
    19         }
    20         //数到m的人出圈
    21         while (true){
    22             if(helper == first){
    23                 break;
    24             }
    25             for (int i = 0; i < (m-1); i++) {
    26                 first = first.getNext();
    27                 helper = helper.getNext();
    28             }
    29             System.out.printf("编号为%d的人出圈\n",first.getNo());
    30             first = first.getNext();
    31             helper.setNext(first);
    32         }
    33         System.out.println("最后出圈的人是:"+first.getNo());
    34 
    35     }

    测试代码

    1 public static void main(String []args){
    2         CycleSingleLinkedList linkedList = new CycleSingleLinkedList();
    3         linkedList.joseph(4,2,3);
    4 }

    测试结果:

     

     

    总结

    以上就是实现约瑟夫问题的循环链表解决思路,当然肯定还有很多别的解决思路,欢迎留言探讨,谢谢。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-1 05:57 , Processed in 0.060218 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表