Java单向链表实现约瑟夫环问题

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

上边的这个故事就是我们约瑟夫问题的由来,约瑟夫问题的简化版,

 在我们实现约瑟夫问题中的一个关键就是,我们如何找到那个要出队的元素,这个元素如何出队列?我们的数据结构采用的是有向单链表,我们要向实现元素的剔除就是要通过这个元素的上一个元素的这个t指针指向这个元素的下一个元素,从而实现将目标元素剔除。又因为我们的元素链表是单向的,如何记住我们目标元素的上一个元素,这时候我们需要一个协助指针。协助指针的初始化也是要是我们开始元素的前一个元素。

下面使我们核心代码的一个分析,关于最后一句的分析是不正确的,我们所有的输出是可以直接在循环体中完成的:

节点类:

class Boy{

    private  int num;
    private  Boy next;

    public Boy(){

    }

    public Boy(int num){
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Boy getNext() {
        return next;
    }

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

约瑟夫环:

class JusepufLinks {
    private Boy firstBoy = null;
    private int boysCount = 0;

    public int getBoysCount() {
        return boysCount;
    }

    public Boy getFirstBoy() {
        return firstBoy;
    }

    public  JusepufLinks(int nums){
        this.boysCount = nums;
        /**
         *@description: 这是一个构造函数,用来构造一个单向循环链表
         *@params: 小孩的数量
         */
        if(nums<1){
            System.out.println("nums,链表数不合法,链表数的最小值为1");
        }else{
            Boy currentBoy = new Boy();
            for (int i = 1; i <= nums; i++) {
                Boy boy = new Boy(i);
                if(i==1){
                    firstBoy = boy;
                    firstBoy.setNext(boy);
                    currentBoy = firstBoy;

                }else{
                    currentBoy.setNext(boy);
                    boy.setNext(firstBoy);
                    currentBoy = boy;
                }
            }
        }
    }

    public int countBoys(){
        int count = 1;
        Boy temp = firstBoy;
        while (temp.getNext()!=firstBoy){
            temp = temp.getNext();
            count++;
        }
        return count;
    }

    public void showJusepufLinks(){
        if(firstBoy==null){
            System.out.println("单向循环链表为空");
        }else{
            Boy showTempBoy = firstBoy;
            do {
                System.out.printf(showTempBoy.getNum()+"->");
                showTempBoy = showTempBoy.getNext();
            }while (showTempBoy.getNext()!=firstBoy);
            System.out.println(showTempBoy.getNum()+"");
        }
    }

    public void testShow(){
        for (int i = 0; i < 20; i++) {
            System.out.println(firstBoy.getNum()+"******************");
            firstBoy=firstBoy.getNext();
        }
    }
}

测试代码,约瑟夫问题核心解决逻辑:

public class JusepufLinksTest{
    public static void main(String[] args) {
        JusepufLinks jusepufLinks = new JusepufLinks(6);
        jusepufLinks.showJusepufLinks();
        jusepuf(jusepufLinks,2,3);
    }

    public static void  jusepuf(JusepufLinks jusepufLinks,int start,int numbers){
        if(jusepufLinks.getFirstBoy()==null&&start<1&&start>numbers){
            System.out.println("所输入的参数不合法请重新输入");
            return;
        }
        //找到开始的那个元素的前一个元素
        Boy startNodePreviously = jusepufLinks.getFirstBoy();
        for (int i = 1; i < jusepufLinks.getBoysCount()+start-1; i++) {
            startNodePreviously = startNodePreviously.getNext();
        }
        //开始的元素
        Boy startNode = startNodePreviously.getNext();
        for (int i = 1; i <jusepufLinks.getBoysCount(); i++) {
            int tagNum = 2;
            while(tagNum<=numbers){
                startNode = startNode.getNext();
                startNodePreviously = startNodePreviously.getNext();
                tagNum ++;
            }
            System.out.printf(startNode.getNum()+"->");
            startNodePreviously.setNext(startNode.getNext());
            startNode = startNodePreviously.getNext();
        }
        System.out.printf(startNode.getNum()+"->");
        System.out.println();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值