第5讲 单向环形链表-约瑟夫问题

103 阅读1分钟

Josephu 问题 Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

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

public class Josepfu {

    public static void main(String[] args) {
        //测试环形链表
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);
        circleSingleLinkedList.display();

        circleSingleLinkedList.countBoy(1,2,5);
    }
}

//创建环形单向链表
class CircleSingleLinkedList{
    //创建first节点
    private Boy first=new Boy(-1);

    //添加小孩节点,构建成一个环形链表
    public void addBoy(int nums){
        if(nums<1){
            System.out.println("nums值不能小于1");
            return;
        }
        Boy curBoy=null; //临时存放小孩
        for (int i = 1; i <= nums; i++) {
            //根据编号创建环形链表
            Boy boy = new Boy(i);
            if(i==1){
                first=boy;
                first.next=first;//构成环装
                curBoy=first;//让curBoy指向第一个小孩
            }else {
                curBoy.next=boy;
                boy.next=first;
                curBoy=boy;
            }
        }
    }

    //遍历环形链表
    public void display(){
        //判断链表是否为空
        if(first==null){
            System.out.println("没有任何小孩~");
            return ;
        }
        //first不能动 使用辅助指针完成遍历
        Boy curBoy=first;
        while (true){
            System.out.println("小孩的编号 "+curBoy.getNo());
            if(curBoy.next==first){
                //遍历完毕
                break;
            }
            curBoy=curBoy.next;//后移
        }
    }

    /**
     *
     * @param startNo  从第几个小孩开始数数
     * @param countNum 表示数几下
     * @param nums     共有最初有多少小孩在圈中
     */

    //根据用户输入计算小孩出圈的顺序
    public void countBoy(int startNo,int countNum,int nums){
        //数据校验
        if(first==null||startNo<1||startNo>nums){
            System.out.println("参数输入有误");
            return ;
        }
        //创建辅助指针 事先指向最后一个节点
        Boy helper=first;

        while (true){
            if(helper.next==first){
                break;//说明已经指向了最后小孩节点
            }
            helper=helper.next;
        }

        //小孩报数前 first和helper移动k-1次
        for (int j=0;j<startNo-1;j++){
            first=first.next;
            helper=helper.next;
        }

        //小孩报数时,first和helper指针同时移动countNum-1次,然后出圈
        //循环操作直到圈中只有一个
        while (true){
            if (helper==first){
                break;//圈中只有一个小孩
            }

            //移动countNum-1
            for (int j = 0; j < countNum-1; j++) {
                first=first.next;
                helper=helper.next;
            }
            //此时firster指向节点 就是出圈的小孩节点
            System.out.println("小孩出圈: "+first.getNo());
            //将first指向的节点小孩出圈
            first=first.next;
            helper.next=first;
        }
        System.out.println("最后留在圈中小孩:"+first.getNo());
    }

}

//节点
class Boy{

    private int no;
    public Boy next;

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

    public int getNo() {
        return no;
    }

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