数据结构——>单向环形链表

一、单向环形链表应用场景

提起单向环形链表,就不得不说约瑟夫问题,约瑟夫环。什么事约瑟夫问题呢?

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

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

3、约瑟夫问题图例
在这里插入图片描述

二、单向环形链表介绍

1、入队过程:
在这里插入图片描述2、出队过程:
在这里插入图片描述

三、单向环形链表代码实现

1、代码实现思路

1、添加节点,构成环装链表

 public void addKid(int nums){
    
    
        //nums数据校验
        if(nums<1){
    
    
            System.out.println("数据错误");
            return;
        }
        //辅助指针,帮助构成环
        Kid curKid=null;
        //使用for循环来创建环形链表
        for (int i = 1; i <=nums ; i++) {
    
    
            //根据编号创建小孩节点
            Kid kid=new Kid(i);
            //如果是第一个小孩
            if(i==1){
    
    
                first=kid;
                first.setNext(first);
                curKid=first;//让curKid指向第一个小孩
            }else{
    
    
                curKid.setNext(kid);//curkid指针指向下一个孩子
                kid.setNext(first);//再指回first
                curKid=kid;
            }
        }
    }

2、遍历当前链表

  public void showKid(){
    
    
        //判断链表是否为空
        if(first==null){
    
    
            System.out.println("链表为空");
            return;
        }
        //因为first不能动,需要创建一个帮助指针来帮助遍历
        Kid curKid=first;
        while (true){
    
    
            System.out.printf("小孩的编号%d\n",curKid.getNo());
            if(curKid.getNext()==first){
    
    //说明已经遍历完毕
                break;
            }
            curKid=curKid.getNext();//curKid向后移
        }
    }

3、计算节点出圈顺序

/**
     *
     * @param startNo   表示从第几个小孩开始数数
     * @param countNum  表示数几下
     * @param nums      表示最初有几个小孩在圈中
     */
    public void countKid(int startNo,int countNum,int nums) {
    
    
        if (first == null || startNo < 1 || startNo > nums) {
    
    
            System.out.println("输入的数据错误");
            return;
        }
        //创建一个辅助指针帮助小孩出圈
        Kid helper = first;
        while (true) {
    
    
            if (helper.getNext() == first) {
    
    //说明已经遍历完毕
                break;
            }
            helper = helper.getNext();
        }
        //让小孩报数前先让frist和helper指针移动k-1次(比如报2次数,实际指针只移动了一下)
        for (int j = 0; j < startNo - 1; j++) {
    
    
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数前,让first和helper指针同时移动m-1次,然后出圈
        while (true) {
    
    
            if (helper == first) {
    
    //说明只有一个节点
                break;
            }
            //让first和helper同时移动countNum-1
            for (int j = 0; j < countNum - 1; j++) {
    
    
                first = first.getNext();
                helper = helper.getNext();
            }
            //first指向的节点就是要出圈的节点
            System.out.printf("出圈的小孩\n", first.getNo());
            //first指向的节点出圈
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈里的节点%d\n", first.getNo());
    }

2、代码实现

完整代码

package sparsearray;

/**
 * @author shkstart
 * @create 2021-08-08 16:22
 */
public class Josepfu {
    
    
    public static void main(String[] args) {
    
    
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addKid(5);

        circleSingleLinkedList.showKid();
        circleSingleLinkedList.countKid(1,2,5);
    }
}
//创建一个环形的单向链表
class CircleSingleLinkedList{
    
    
    //创建一个first节点,当前没有编号
    private Kid first=null;
    //添加小孩节点,构成环装链表
    public void addKid(int nums){
    
    
        //nums数据校验
        if(nums<1){
    
    
            System.out.println("数据错误");
            return;
        }
        //辅助指针,帮助构成环
        Kid curKid=null;
        //使用for循环来创建环形链表
        for (int i = 1; i <=nums ; i++) {
    
    
            //根据编号创建小孩节点
            Kid kid=new Kid(i);
            //如果是第一个小孩
            if(i==1){
    
    
                first=kid;
                first.setNext(first);
                curKid=first;//让curKid指向第一个小孩
            }else{
    
    
                curKid.setNext(kid);//curkid指针指向下一个孩子
                kid.setNext(first);//再指回first
                curKid=kid;
            }
        }
    }


    //遍历当前链表
    public void showKid(){
    
    
        //判断链表是否为空
        if(first==null){
    
    
            System.out.println("链表为空");
            return;
        }
        //因为first不能动,需要创建一个帮助指针来帮助遍历
        Kid curKid=first;
        while (true){
    
    
            System.out.printf("小孩的编号%d\n",curKid.getNo());
            if(curKid.getNext()==first){
    
    //说明已经遍历完毕
                break;
            }
            curKid=curKid.getNext();//curKid向后移
        }
    }

    //计算小孩出圈顺序
    /**
     *
     * @param startNo   表示从第几个小孩开始数数
     * @param countNum  表示数几下
     * @param nums      表示最初有几个小孩在圈中
     */
    public void countKid(int startNo,int countNum,int nums) {
    
    
        if (first == null || startNo < 1 || startNo > nums) {
    
    
            System.out.println("输入的数据错误");
            return;
        }
        //创建一个辅助指针帮助小孩出圈
        Kid helper = first;
        while (true) {
    
    
            if (helper.getNext() == first) {
    
    //说明已经遍历完毕
                break;
            }
            helper = helper.getNext();
        }
        //让小孩报数前先让frist和helper指针移动k-1次(比如报2次数,实际指针只移动了一下)
        for (int j = 0; j < startNo - 1; j++) {
    
    
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数前,让first和helper指针同时移动m-1次,然后出圈
        while (true) {
    
    
            if (helper == first) {
    
    //说明只有一个节点
                break;
            }
            //让first和helper同时移动countNum-1
            for (int j = 0; j < countNum - 1; j++) {
    
    
                first = first.getNext();
                helper = helper.getNext();
            }
            //first指向的节点就是要出圈的节点
            System.out.printf("出圈的小孩\n", first.getNo());
            //first指向的节点出圈
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈里的节点%d\n", first.getNo());
    }
}
//创建Kid类,表示节点
class Kid{
    
    
    private int no;//小孩编号
    private Kid next;//表示下一个节点

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

    public int getNo() {
    
    
        return no;
    }

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

    public Kid getNext() {
    
    
        return next;
    }

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

おすすめ

転載: blog.csdn.net/weixin_46457946/article/details/119533810