淘先锋技术网

首页 1 2 3 4 5 6 7

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。(最后只能活下来一个人,他还安排他朋友干嘛???)

看了问题的描述我们得到了以下关键信息。

1、是一个圈。

2、从1开始,数到第三个人,把第三个人去掉。然后从下一个重置,继续数到第三个人,去掉,

3、只剩最后一个人时停止。

我们可以使用循环链表来实现这个问题(最后一个元素的next为第一个),用一个不带头结点的循环链表来处理Josephus问题:先构成一个有 n 个结点的单循环链表,然后由从第一个起从1开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到只剩最后一个人。
首先我们来创建一个循环链表,思路如下
1、先创建一个节点,让first指向该节点,让first的next也是first,形成自循环。
2、后面没创建一个新的节点,就把该节点加入现有的循环中。(currboy为辅助变量,用于形成循环链表)
如下图所示:

代码如下:

    public void add(int nums) { //要整几个节点的环形链表
        if (nums < 0) {
            System.out.println("nums数值不准确");
            return;
        }
        BoyNode currBoy = new BoyNode();//辅助指针,帮助构建环形链表
        for (int i = 0; i < nums; i++) {
            BoyNode boy = new BoyNode(i);
            if (i == 0) {
                first = boy;
                first.setNext(first);//构成环
                currBoy = first;//当前
            } else {
                currBoy.setNext(boy);//上一个指向当前
                boy.setNext(first);//永远指向第一个构成环
                currBoy = boy;//当前后移
            }
        }
    }

 然后我们进行第二步,找最后活下来的那个人,首先我们判断一下first为不为空,不为空的话,使用辅助变量currboy遍历,设置一个flag来记录当前遍历到第几个了(记录的是比如第三个人kill,记录到第三个就会重置。)然后循环,当fllag==m时,让currboy.next=currboy.next.next(删除第m个元素),同时将flag重置,然后辅助变量后移,结束条件为currboy.next.no=currboy.no。

见下图

 代码如下:

    public void Joseph(int killnum){
        if (first==null){
            System.out.println("此环形链表不存在");
        }
        BoyNode currBoy=new BoyNode();
        currBoy=first;
        int flag=1;
        while (true){
            flag++;
            if (flag==killnum){
                System.out.println("杀掉=>"+currBoy.getNext().getNo());
                currBoy.setNext(currBoy.getNext().getNext());
                flag=1;
            }
            if (currBoy.getNext().getNo()==currBoy.getNo()){
                System.out.println("最后活下来的是"+currBoy.getNo());
                break;
            }
            currBoy=currBoy.getNext();
        }
    }

完整代码如下:

package datastructure.linkedlist.JosephDemo;




public class JosephDemo {
    public static void main(String[] args) {
        CycleSingleLinkedList cycleSingleLinkedList=new CycleSingleLinkedList();
        //总人数
        cycleSingleLinkedList.add(6);
        cycleSingleLinkedList.list();
        cycleSingleLinkedList.Joseph(3);
    }

}

class CycleSingleLinkedList {
    private BoyNode first = new BoyNode();

    public void add(int nums) { //要整几个节点的环形链表
        if (nums < 1) {
            System.out.println("nums数值不准确");
            return;
        }
        BoyNode currBoy = new BoyNode();//辅助指针,帮助构建环形链表
        for (int i = 1; i <= nums; i++) {
            BoyNode boy = new BoyNode(i);
            if (i == 1) {
                first = boy;
                first.setNext(first);//构成环
                currBoy = first;//当前
            } else {
                currBoy.setNext(boy);//上一个指向当前
                boy.setNext(first);//永远指向第一个构成环
                currBoy = boy;//当前后移
            }
        }
    }

    // 遍历当前的环形链表
    public void list(){
        if (first==null){
            System.out.println("此环形链表不存在");
        }
        BoyNode currBoy=new BoyNode();
        currBoy=first;
        while (true){
            if (currBoy.getNext()==first){
                System.out.println(currBoy);
                break;
            }
            System.out.println(currBoy);
            currBoy=currBoy.getNext();
        }
    }

    //约瑟夫环
    public void Joseph(int killnum){
        if (first==null){
            System.out.println("此环形链表不存在");
        }
        BoyNode currBoy=new BoyNode();
        currBoy=first;
        int flag=1;
        while (true){
            flag++;
            if (flag==killnum){
                System.out.println("杀掉=>"+currBoy.getNext().getNo());
                currBoy.setNext(currBoy.getNext().getNext());
                flag=1;
            }
            if (currBoy.getNext().getNo()==currBoy.getNo()){
                System.out.println("最后活下来的是"+currBoy.getNo());
                break;
            }
            currBoy=currBoy.getNext();
        }
    }
}

class BoyNode {
    private int no;//编号
    private BoyNode next;//指向下一个节点 如果没有则为空

    public BoyNode() {
    }

    public BoyNode(int no) {
        this.no = no;
    }


    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public BoyNode getNext() {
        return next;
    }

    public void setNext(BoyNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "LinkedList{" +
                "no=" + no +
                '}';
    }
}

运行结果如下:

 

 

这种方式时间复杂度较高,有一种公式法可参考:换个角度举例解决约瑟夫环 - 圆圈中最后剩下的数字 - 力扣(LeetCode)