【数据结构】应用循环链表解决约瑟夫问题(无头节点)

文章目录

  • 什么是约瑟夫问题?
  • 添加元素
  • 约瑟夫核心问题

什么是约瑟夫问题?

首先我们先看个故事:

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

声明一个类代表人,因为是单项链表,所以在该类中需要有一个指向下一个节点的指针

/**
 * 创建一个Boy类,表示一个节点
 */
class Boy{
    private int no;//编号
    private Boy next;//指向下一个节点,默认null

    public Boy (int no) {
        this.no = no;
    }
    //此处getter和setter方法省略
}

添加元素

  • 1.初始状态下,循环链表只有一个指针,我们声明这个指针为first,因为没有元素,所以这个指针是null

  • 2.第一次添加元素时,假定这个元素是 (1-关羽),那么我们就应该让first指针指向 (1-关羽) 这个元素,因为是循环链表,所以元素 (1-关羽) 的指针(假定定义为next指针)是指向自己的,如下图:
    【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第1张图片

  • 3.第二次添加元素时,假定这个元素是 (2-张飞) 需要一下步骤:

    • 第一步:张飞的next指针=关羽的next指针
    • 第二步:关羽的next指针=张飞

【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第2张图片

4.第三次在添加元素时,假定这个元素是 (3-赵云) ,那么此时就按照第3步,就ok

总结:添加元素主要分为两部分:1.添加第一元素,自己构成环;2.继续添加元素时,加到环上即可。

/**
 * 创建一个环形的单项链表
 */
class CircleSingleLinkedList{
    //创建一个first节点
    private Boy first;


    /**
     * 添加小孩节点,构建成一格环形的链表
     * @param nums
     */
    public void addBoy(int nums){
        //一、对参数进行校验
        if(nums < 1 ){
            System.out.println("nums的值不正确");
            return;
        }
        //因为first指针不能动,所以定义一个指针,来代替first指针
        Boy curBoy = null;
        //使用for来创建我们的环形链表
        for (int i = 1; i <= nums; i++) {
            //根据编号,创建小孩节点
            Boy boy = new Boy(i);
            //情况一:当前链表中无元素
            if(i == 1){
                first = boy;
                first.setNext(first);//构建环状
            } else {
            //情况二:当前链表中有元素
                curBoy.setNext(boy);
                boy.setNext(first);
            }
            curBoy = boy;
        }
    }
}

约瑟夫核心问题

因为约瑟夫的人数一共是39+约瑟夫+它的朋友,一共是41个,人数太多画图很复杂,所以以5人为例进行研究。

  • 1.首先准备五个人,寻找一个开始节点 (这里我们就以三国蜀国五虎上将之首关羽作为第一个节点)

【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第3张图片

  • 2.因为需要取出元素,而且链表是单向链表,所以必须定义一个辅助变量表示尾指针 (定义为last指针) ,否则无法获取到元素的前一个节点
    ·【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第4张图片

  • 3.首先是关羽喊1号,张飞喊2号,然后赵云喊3号,(喊号就是移动last和head指针,只不过,需要移动喊号的次数-1,因为指针本身就在关羽这里) 此时的head指针指向赵云,last指针指向张飞

    • 第一步:赵云出队列
    • 第二步:让张飞的next=赵云的next,即:last.next=head.next;
    • 第三步:继续喊号,即:last指针和head指针继续移动
      【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第5张图片
  • 4.依次循环,直到last.next=head.next,此时证明链表中就只有一个元素了,此时就结束了


/**
 * 创建一个环形的单项链表
 */
class CircleSingleLinkedList{
    //创建一个first节点
    private Boy first;


    /**
     * 根据用户的输入,加算出小孩出圈的顺序
     * @param startNo 表示从第几个小孩开始数数
     * @param countNum 表示数几下
     * @param nums 表示初始有多少小孩在权重
     */
    public void countBoy(int startNo , int countNum , int nums){
        //一、参数校验
        if(first == null || startNo < 1 || startNo > nums || countNum > nums){
            System.out.println("参数有误,请重新输入");
            return;
        }
        //定义一个辅助指针last,并辅助赋值,让他指向循环链表的末尾元素
        Boy last = first;
        while (last.getNext() != first){
            last = last.getNext();
        }


        //定义一个临时变量,指向首元素
        Boy head = first;
        //因此此时head是第一个元素,last是最后一个元素,所以只要让这两个元素同时移动,即可保证相对位置是head在前,last元素在其后面
        for (int i = 0; i < startNo-1; i++) {
            head = head.getNext();
            first = first.getNext();

        }


        //遍历要数的次数,直到遍历剩下一个元素为止
        while (head != last){
            for (int i = 0; i < countNum-1 ; i++) {
                head = head.getNext();
                last = last.getNext();
            }
            System.out.println("出队列的人:"+head.getNo());

            //让head元素向后移动
            head = head.getNext();
            last.setNext(head);
        }
        System.out.println("最后剩下的人:"+head.getNo());
    }
}
  • 5.编写一个测试类测试一下:
public class JosepfuDome {
    public static void main(String[] args) {
        //测试添加和遍历是否ok
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(41);
     	//第一个形参是:从那个元素开始报数
     	//第二个形参是:第几次报数的时候开始出队列
     	//第三本形参是:一共有多少人
        circleSingleLinkedList.countBoy(1,3,41);
    }
}

D:\JAVA\jdk-8u131-windows-x64\bin\java.exe -javaagent:D:\InteIIiJ_IDEA\IntelliJ_IDEA_2019.2.3\lib\idea_rt.jar=57698:D:\InteIIiJ_IDEA\IntelliJ_IDEA_2019.2.3\bin -Dfile.encoding=UTF-8 -classpath D:\JAVA\jdk-8u131-windows-x64\jre\lib\charsets.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\deploy.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\access-bridge-64.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\cldrdata.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\dnsns.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\jaccess.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\jfxrt.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\localedata.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\nashorn.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\sunec.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\sunjce_provider.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\sunmscapi.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\sunpkcs11.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\ext\zipfs.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\javaws.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\jce.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\jfr.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\jfxswt.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\jsse.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\management-agent.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\plugin.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\resources.jar;D:\JAVA\jdk-8u131-windows-x64\jre\lib\rt.jar;G:\java_study\DataStructuresArithmetic\out\production\DataStructuresArithmetic com.atguigu.linkedlist.JosepfuDome
打印出来的孩子是:3
打印出来的孩子是:6
打印出来的孩子是:9
打印出来的孩子是:12
打印出来的孩子是:15
打印出来的孩子是:18
打印出来的孩子是:21
打印出来的孩子是:24
打印出来的孩子是:27
打印出来的孩子是:30
打印出来的孩子是:33
打印出来的孩子是:36
打印出来的孩子是:39
打印出来的孩子是:1
打印出来的孩子是:5
打印出来的孩子是:10
打印出来的孩子是:14
打印出来的孩子是:19
打印出来的孩子是:23
打印出来的孩子是:28
打印出来的孩子是:32
打印出来的孩子是:37
打印出来的孩子是:41
打印出来的孩子是:7
打印出来的孩子是:13
打印出来的孩子是:20
打印出来的孩子是:26
打印出来的孩子是:34
打印出来的孩子是:40
打印出来的孩子是:8
打印出来的孩子是:17
打印出来的孩子是:29
打印出来的孩子是:38
打印出来的孩子是:11
打印出来的孩子是:25
打印出来的孩子是:2
打印出来的孩子是:22
打印出来的孩子是:4
打印出来的孩子是:35
打印出来的孩子是:16
最后留在圈中的小孩是:31

Process finished with exit code 0

我们看到最后的结果的确是:剩下16和31,所以最后站在16和31号的约瑟夫和它的朋友存活了下来。

怎么样?要不要来个可乐庆祝一下,又学废了。。。
【数据结构】应用循环链表解决约瑟夫问题(无头节点)_第6张图片

你可能感兴趣的:(数据结构与算法,链表,约瑟夫问题,java,数据结构)