约瑟夫问题

约瑟夫问题

约瑟夫问题有时也称为约瑟夫斯置换,类似问题又称为约瑟夫环。又称“丢手绢问题”.

问题由来

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

在这里插入图片描述

问题转换:

41个人坐一圈,第一个人编号为1,第二个人编号为2,第n个人编号为n。
1、编号为1的人开始从1报数,依次向后,报数为3的那个人退出圈;
2、自退出那个人开始的下一个人再次从1开始报数,以此类推;
3、求出最后退出的那个人的编号。

提示:
用一个循环链表来处理Josephu问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开’始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。

解决问题:

1、构建含有41个结点的单向循环链表,分别存储1~41的值,分别代表这41个人;
2、使用计数器count,记录当前报数的值;
3、遍历链表,每循环一次,count++;
4、判断count的值,如果是3,则从链表中删除这个结点并打印结点的值,把count重置为0;
在这里插入图片描述
代码:
1、节点类

public class Node<T>{
    //存储数据
    T element;
    //下一个节点
    Node next;

    public Node(T element, Node next) {
        this.element = element;
        this.next = next;
    }
}

2、测试代码

public class JosephTest {

    public static void main(String[] args) {

        //1、构建循环链表,包含41个节点,分别存储1~41之间的值
        //记录首节点
        Node<Integer> first = null;
        //记录下一个节点
        Node<Integer> down = null;

        for (int i = 1; i <=41 ; i++) {
            //如果是第一个节点
            if(i==1){
                first = new Node<>(i,null);
                down = first;
                continue;
            }
            //如果不是第一个节点
            Node<Integer> newNode = new Node<>(i, null);
            down.next = newNode;
            down = newNode;
            //如果是最后一个节点,让最后一个节点的下一个节点为frist
            if(i==41){
                down.next=first;
            }
        }

        //2、定义一个计数器,模拟报数
        int count = 0;
        //3、遍历循环链表

        //记录每次遍历拿到的节点,第一次是首节点
        Node<Integer> now = first;

        //当前节点的上一个节点
        Node<Integer> before = null;

        //如果不是只有一个节点就继续遍历
        while (now!=now.next){
            //模拟报数
            count++;
            //判断当前节点是不是3
            if(count==3){
                //如果是3,移除当前节点,并打印,重置计数器,让当前节点后移
                before.next = now.next;
                System.out.print(now.element+",");
                count = 0;
                now = now.next;
            }else {
                //如果不是3,让before为当前节点,并让当前节点下移
                before = now;
                now = now.next;
            }
        }

        //打印最后一个元素
        System.out.println(now.element);
    }   
}

3、测试结果:
在这里插入图片描述
最后两个数为:16,31。

约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值