JAVA数据结构 - 单向环形链表与约瑟夫问题

约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)

N个人围成一圈,从第X个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。

 例如N=5,X=1,  M=2,被杀掉的顺序是:2 , 4 , 1 , 5 , 3

Java环形链表实现

   1.模拟链表的 单个节点的数据结构

class Boy {

    private int number;       小孩编号
    private Boy next;        小孩下一位 

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

    public Boy getNext() {
        return next;}

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

 2. 添加小孩并形成环

    1 .定一个辅助 cur 指针   始终指向最新添加进来的小孩          2.当第一个小孩时,自己指向自己         

    3.当不是第一个时,  将 cur 的下一位指向新添加进来的 ,将新添加进来的 指向第一个形成环  ,   将cur后移指向自己

  public void add(int num) {
        if (num < 1) {
            System.out.println("数量太小!");
            return;
        }

        Boy cur = null;     //定一个辅助指针变量

        for (int i = 1; i <= num; i++) {
            if (i == 1) {
                first = new Boy(1);          //如果是第一个 就定义第一个小孩
                first.setNext(first);         //将自己指向自己
                cur = first;                  //将当前指向first , 后面继续使用
            } else {
                Boy boy = new Boy(i);      //不是第一个,根据编号创建
                cur.setNext(boy);          //将当前指针的下一位指向,新来的
                boy.setNext(first);         //将新来的指向第一个,形成环
                cur = boy;                   //将当前指向新来的 , 后面继续使用
}}}

3.实现约瑟夫问题

    1. 因为是单向的环形链表,所以定一个辅助指针 helper 始终指向  cur 后面的一位

    2.先让  helper cur 同时移动   startNumber-1个      因为数数自己也算一个

    3.开始剔除时,结束条件 :  cur== helper         

    4.数数时    helper cur同时移动 countNumber - 1 个 后         cur就是需要出圈的小孩

            cur=cur.getNext();          让cur后移一位, 即下一位开始报数的人
            helper.setNext(cur);        让helper指向 出圈的小孩的下一个, 即下一位开始报数的人,就是让当前first小孩出圈

 

    5.first为第一个加入的小孩  

       1.第一循环将helper 放在first 后面去             2.for循环将first, helper  同时移动 startnumber-1个,即从第几个开始数

       3.开始数数,  如果first==helper 说明到了最后一个结束循环  将first, helper  同时移动 countnumber-1个 ,first指向的为出圈的

       4.将first后移,将helper 指向新的first,即将旧的first出圈

    public void ShowJosephProblem(int startNumber, int countNumber, int BoyNumbers) {
        this.add(BoyNumbers);
        if (startNumber < 1 || startNumber > BoyNumbers) {
            System.out.println("参数非法");
            return;
        }

        Boy helper = first;    //先让helper指向first  再循环使他指向最后一个

        while (true) {
            if (helper.getNext() == first) {
                break;
            }
            helper = helper.getNext();
        }

        //让first 指向 第一个开始数数的小孩
        for (int i = 0; i < startNumber - 1; i++) {   //因为自己数算一个,所以是移动 countNumber-1个
            helper = helper.getNext();
            first = first.getNext();
        }

        //开始数数,
        while (true){

            if (first ==helper){   //因为 helper 跟在后面  当最后一个小孩时他们相等
                break;
            }

            //让first,helper同时移动countNumber- 1个后  ,现在first 指向需要出圈的小孩,helper指向它的前一个
            for (int i = 0; i < countNumber - 1; i++) {
                first = first.getNext();
                helper=helper.getNext();
            }

            System.out.printf("当前出圈小孩编号: %d \n", first.getNumber());

            first = first.getNext();   //让first后移一位, 即下一位开始报数的人
            helper.setNext(first);  //让helper指向 出圈的小孩的下一个, 即下一位开始报数的人,就是让当前first小孩出圈
        }
        System.out.printf("最后小孩编号: %d", first.getNumber());        }}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章