(数据结构与算法)单向环形链表解决约瑟夫问题

约瑟夫(Josephu)问题

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

解决思路

用一个不带头结点的循环链表解决:先构成一个带有n个节点的单向环形链表,然后由k节点起从1开始计数,计到m时,对应节点从链表中删除,然后再从被删除节点的下一个节点开始数,直到最后一个节点从链表中删除。

代码实现

单向循环链表 和单链表区别在于单向循环链表的最后一个节点指向第一个节点,且没有头结点。
在这里插入图片描述

//约瑟夫问题单向环形链表解决
public class CircleSingleLinkedListDemo {
    public static void main(String[] args) {
        CircleSingleLinkedList list = new CircleSingleLinkedList();
        list.addBoy(10);
        list.traverseBoy();
        list.countBoy(2,2,10);
    }
}


class CircleSingleLinkedList{
    public Boy first = null;//第一个节点

    /**
     * 添加num个节点
     * @param num
     */
    public void addBoy(int num){
        if (num<1){
            System.out.println("num的值不正确");
            return;
        }
        Boy curBoy = null;//辅助变量,帮助遍历
        for (int i = 1; i <=num ; i++) {
            Boy  boy= new Boy(i);//创建节点
            if (i == 1){
                //如果为第一个节点给第一个节点赋值
                first=boy;
                first.setNext(boy);
                //cirBoy指向当前的first
                curBoy = first;
            }else {
                //将当前节点指向下一个节点添加的节点boy
                curBoy.setNext(boy);
                //将最后一个节点指向第一个节点,形成环形链表
                boy.setNext(first);
                //curBoy后移一位
                curBoy = boy;
            }
        }
    }

    /**
     * 遍历环形链表
     */
    public  void traverseBoy(){
        if (first==null){
            System.out.println("环形链表为空");
            return;
        }
        Boy curBoy = first;//辅助变量,用于遍历
        System.out.print("遍历结果:");
        while (true){
            System.out.print(curBoy.getNo()+" ");
            if (curBoy.getNext()==first){
                break;
            }
            //当前节点curBoy后移一位
            curBoy= curBoy.getNext();
        }
        System.out.println();
    }

    /**
     *
     * @param startNo   表示从第几个小孩开始数
     * @param countNum  表示每次数几下
     * @param nums      表示一共有几个小孩
     */
    public void countBoy (int startNo,int countNum,int nums){
        if (first ==null || startNo<1 ||countNum>nums){
            System.out.println("参数输入有误");
            return;
        }
        System.out.print("出圈顺序:");
        //先让first节点和开始节点为同一个节点
        for (int i = 0; i <startNo - 1 ; i++) {
            first = first.getNext();
        }
        //创建辅助变量helper指向最后一个节点
        Boy helper =first;
       while (true){
           if (helper.getNext()==first){
               //遍历结束
               break;
           }
           helper= helper.getNext();
       }
       //让firs和helper同时移动countNum -1次数后,first就为要出圈的那个节点
       //循环操作,直到圈中只有一个节点
        while (true) {
            if (first == helper){
                //圈中只有一个结点
                break;
            }
            //firs和helper同时移动countNum -1次
            for (int i = 0; i < countNum - 1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //first就为要出圈的那个节点
            System.out.print(first.getNo()+"-->");
            //删除出圈的节点
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.println(first.getNo());
    }
}

class Boy{
    private  int no;//节点编号
    private  Boy next; //下一个节点,默认为空

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

    public int getNo() {
        return no;
    }

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

    public Boy getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "Boy{" +
                "no=" + no +
                '}';
    }
}

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值