【Java】单循环链表解决约瑟夫环问题

问题描述

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

数据结构

辅助解决问题的数据结构:
单向循环链表

编写结点类

本例中用Integer而放弃了泛型

public class LinkedNode {
    
    private Integer data;
    
    private LinkedNode next;
    
    public LinkedNode() {
        this.data = null;
        this.next = null;
    }
    
    public LinkedNode(Integer element) {
        this.data = element;
        this.next = null;
    }

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    public LinkedNode getNext() {
        return next;
    }

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

}

约瑟夫环的链表实现(单向循环链表)

public class CircularSinglyLinkedList {
    
    /**
     * 尾引用
     */
    protected LinkedNode rear;
    
    /**
     * 头引用
     */
    protected LinkedNode first;
    
    public CircularSinglyLinkedList() {
        rear = null;
    }
    
    public void joseph(int n, int m) {
        //createCircular函数初始化n个结点的约瑟夫环
        createCircular(n);
        //初始化pre为表尾
        LinkedNode pre = rear;
        //初始化p为表头
        LinkedNode p = rear.getNext();
        //初始化计数器count
        int count = 1;
        System.out.println("出环的顺序为:");
        //循环到环中只剩一个结点
        while (p.getNext() != p) {
            //计数器未累加到密码值
            if (count < m) {
                pre = p;
                p = p.getNext();
                count++;
            } else {    //累加器加到密码值
                System.out.print(p.getData() + " ");
                //删除p结点
                pre.setNext(p.getNext());
                //p赋为pre当前后继
                p = pre.getNext();
                //count赋值1,重新计数
                count = 1;
            }
        }
        //输出最后一个结点编号
        System.out.print(p.getData() + " ");
    }
    
    /**
     * 根据参数n生成循环单链表
     * @param n
     */
    protected void createCircular(int n) {
        //生成第一个结点,赋给尾引用
        rear = new LinkedNode(1);
        first = rear;
        //依次尾插入2、3、...、n新结点
        for (int i = 2; i <= n; i++) {
            LinkedNode node = new LinkedNode(i);
            rear.setNext(node);
            rear = node;
        }
        //尾结点引用赋值为头结点,形成循环链表
        rear.setNext(first);
    }

}

测试

public class JosephTester {
    
    public static void main(String[] args) {
        CircularSinglyLinkedList list = new CircularSinglyLinkedList();
        list.joseph(5, 3);
    }

}

测试结果

出环的顺序为:
3 1 5 2 4 
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星拱北辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值