08 数据结构与算法之单向环形链表

单向环形链表应用场景

Josephu(约瑟夫、约瑟夫环)问题:

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

不带头节点的单向环形链表示意图:
在这里插入图片描述
构建一个单向环形链表的思路:

  1. 先创建一个节点,让 first 指向该节点,并形成环形
  2. 后面当我们每创建一个新的节点,就把该节点加入到已有的环形链表中。

代码实现:

//创建环形的单向链表
class CircleSingleLinkedList {
    private Boy first;

    //添加
    public void addBoys(int nums) {
        //对nums做一个简单的数据校验
        if(nums < 1)
        {
            System.out.println("nums的值不正确");
            return ;
        }
        Boy curBoy = null;
        //使用for循环来创建我们的环形链表
        for(int i = 1; i<=nums; i++){
            Boy temp = new Boy(i);
            if (i == 1){
                first = temp;
                first.setNext(first);
                curBoy = first;
            } else {
                temp.setNext(curBoy.getNext());
                curBoy.setNext(temp);
                curBoy = curBoy.getNext();
            }
        }
    }

    //遍历当前环形链表
    public void showBoys(){
        if (first == null) {
            System.out.println("链表为空");
            return ;
        }
        Boy temp = first;
        while(temp.getNext() != first){
            System.out.println(temp);
            temp = temp.getNext();
        }
        System.out.println(temp);
    }

    //解决约瑟夫问题
    //根据用户的输入,生成一个小孩出圈的顺序
    //n = 5,即有5个人
    //k = 1,从第一个人开始报数
    //m = 2,数两下
    //思路:
    //1、创建一个辅助指针helper,事先指向环形链表的最后一个节点
    //2、小孩报数时,让first和helper同时移动 m - 1次
    //3、first指向的小孩节点出圈
    // first = first.next  helper.next = first

    /**
     *
     * @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;
        //helper移动到first的上一个节点
        while(helper.getNext() != first)
            helper = helper.getNext();
        //first移动到开始节点,helper同步移动
        while(first.getNo() != startNo){
            first = first.getNext();
            helper = helper.getNext();
        }
        //开始出圈
        while(first.getNext() != first){
            //当first指向自己时,说明只有1个小孩了
            //这里也可以用条件:first != helper
            int count = 1;
            //开始数
            while(count != countNum){
                first = first.getNext();
                helper = helper.getNext();
                count++;
            }
            //first这个小孩出圈
            System.out.printf("小孩%d出圈\n", first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        //最后一个留下来的小孩出圈
        System.out.printf("小孩%d出圈\n", first.getNo());
    }
}

//创建一个Boy类,表示一个节点
class Boy{
    private int no;  //编号
    private Boy next;  //指向下一个节点,默认为null

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

    public int getNo() {
        return no;
    }

    public Boy getNext() {
        return next;
    }

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

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

    @Override
    public String toString(){
        return "[ no = " + no + "]";
    }
}

测试一下:

public class Josepfu {
    public static void main(String[] args){
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoys(125);
//        circleSingleLinkedList.showBoys();
        circleSingleLinkedList.countBoy(1, 2, 125);
    }
}

输出:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值