其他分享
首页 > 其他分享> > 快速上手环形链表解决约瑟夫问题

快速上手环形链表解决约瑟夫问题

作者:互联网

本文思路:
1:什么是环状链表,应用在哪里?
2:如何构造环状链表
3:双指针解决环状链表问题
4:总结
5:分享歌曲
注意,本篇只说单链表组成的环状链表

什么是环状链表

首先可以先回顾一下什么是单链表,我们比葫芦画瓢去学习环状链表。
单链表大概长这样:
在这里插入图片描述
很容易看出来大概是以头节点领着的一条线,首尾没有串起来。
那么环状链表就是把首尾串起来而已,大概长这样:
在这里插入图片描述
环状链表和单链表的区别,就是他们的连接结构而已,其实双向链表也是可以组成环状链表的,以后我们在细说,饭我们一口一口吃。

如何构造环状链表

构建规则:
1:首节点和末节点被连接在一起
2:节点为单链表的节点
3:双指针,一个辅助指针helper,一个first指向指针。
4:先创建第一个节点,让first指向该节点,形成环形
5:后面每增加一个节点,就加入到该环形里。

先解释一下简单出现的情况:
1:添加链表
首先展示一个初始情况:
在这里插入图片描述
这个时候我们设置指针,将它自己首尾相连,同时增加一个辅助指针,它用于后面添加指针时,first指向最新的,而此时辅助指针留在之前最后的节点,可以连接新的节点。
在这里插入图片描述
现在假设增加一个链表:
a:初始情况
在这里插入图片描述
b:随后指针开始指向构建:
1:(过程开始)first指针一直指向头链表,方便新链表指向first指针
2:辅助指针首先将指向的本链表连接新链表(过程q),随后移动到新链表中(过程w)
走到过程q时:
在这里插入图片描述
走到过程w时:
在这里插入图片描述
链表结构代码如下:

public class people {
    private int id;
    private String name;
    private people next;

    public people(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public people getNext() {
        return next;
    }

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

构建环状链表代码:

public class CircleSingleLInkedList {
    public static void main(String[] args) {
        CircleSingleLInkedList list =new CircleSingleLInkedList();
        list.addpeople(6);
        list.showPeo();
    }
 private people first =new people(-1,"-1");//这里赋值什么编号无所谓,后面会重新赋值正确的值
    //添加链表
    public void addpeople(int peos){
        if(peos<1){
            System.out.println("peos的值不规范");
            return;
        }
        people curpeo =null;//辅助指针,帮助构建环状链表
        for(int i=1;i<= peos;i++){
            people peo = new people(i,"w"+i);
            if(i==1){   //第一个链表要特殊处理,先构成环!
                first=peo;
                first.setNext(first);//构成环状
                curpeo=first;//让辅助指针指向第一个人
            }else {
                curpeo.setNext(peo);//之前最后一个节点连接新的节点
                peo.setNext(first);//新的节点连接第一个节点,重新构成环
                curpeo=peo;//最后辅助指针指向最后一个节点
            }
        }
    }
    //遍历环形链表
    public void showPeo(){
        if(first==null){
            System.out.println("没有任何数据");
            return;
        }
        //遍历节点时,first指针不动,使用辅助节点完成
        people peo= first;
        while (true){
            System.out.println("链表"+peo.getId());
            if(peo.getNext()==first){
                System.out.println("遍历完毕");
                break;
            }
            peo=peo.getNext();//辅助指针后移
        }
    }
}

结果:
在这里插入图片描述

双指针解决环状链表问题

经典案例,约瑟夫问题。
约瑟夫问题:
在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
用环状链表来解决或者数组也行,这里介绍环状链表的双指针解决法,个人感觉比快慢指针简单一些。
过程如果上面看明白的话,这里应该就明白了,所以直接给出代码,有哪个地方不明白的可以看看上文,应该都提到过。
代码如下:

public class CircleSingleLInkedList {


    public static void main(String[] args) {
        CircleSingleLInkedList list =new CircleSingleLInkedList();
        list.addpeople(5);
        list.showPeo();
        list.killpeo(1,2,5);
    }
    private people first =new people(-1,"-1");//这里赋值什么编号无所谓,后面会重新赋值正确的值
    //添加链表
    public void addpeople(int peos){
        if(peos<1){
            System.out.println("peos的值不规范");
            return;
        }
        people curpeo =null;//辅助指针,帮助构建环状链表
        for(int i=1;i<= peos;i++){
            people peo = new people(i,"w"+i);
            if(i==1){
                first=peo;
                first.setNext(first);//构成环状
                curpeo=first;//让辅助指针指向第一个人
            }else {
                curpeo.setNext(peo);//之前最后一个节点连接新的节点
                peo.setNext(first);//新的节点连接第一个节点,重新构成环
                curpeo=peo;//最后辅助指针指向最后一个节点
            }
        }
    }

    //遍历环形链表
    public void showPeo(){
        if(first==null){
            System.out.println("没有任何数据");
            return;
        }

        //遍历节点时,first指针不动,使用辅助节点完成
        people peo= first;
        while (true){
            System.out.println("链表"+peo.getId());
            if(peo.getNext()==first){
                System.out.println("遍历完毕");
                break;
            }
            peo=peo.getNext();//辅助指针后移
        }
    }
    //startId:表示从第几个人开始数;countNum:表示数几次;nums:表示最初有多少人
    public void killpeo(int startId,int countNum,int nums){
        if(first==null||startId<1||startId>nums){
            System.out.println("你敢戏弄我们,先鲨了你");
            return;
        }
        //辅助指针,帮助犹太人自杀
        people helper =first;
        while(true){
            if(helper.getNext()==first){  //指向最后一个犹太人
              break;
            }
            helper=helper.getNext();
        }
        for(int k=0;k<startId-1;k++){//先让first和helper指针移动k-1次,此时first指针刚好指向编号为startId的这个人,而辅助指针在它前面一位
            first=first.getNext();
            helper=helper.getNext();
        }
        //开始报数时,让first和helper指针同时移动n-1次,然后自杀
        while (true){
            if(helper==first){//圈中只剩一个人
                break;
            }
            for(int l=0;l<countNum-1;l++){
                first=first.getNext();
                helper=helper.getNext();
            }
            System.out.println("第"+first.getId()+"犹太人自杀了");
            //将自杀的人移出圈
            first=first.getNext();
            helper.setNext(first);
        }
        System.out.println("最后一个自杀的人是编号"+first.getId());
    }

}

结果:
在这里插入图片描述

总结

环形链表要明白中间的过程和指针的变换,我感觉收获挺大的,双指针的思考方法在很多地方都有应用,学习意义很好,学习的时候注意一下指针的指向问题。

分享歌曲

飞机场的10:30
-陶喆
喜欢原因:陶喆我最最喜欢的歌手之一,他的所有歌我都喜欢,喜欢听他的整张专辑,这首歌真是直接封神,太喜欢这张专辑了,坐高铁或者火车地铁都会直接从专辑第一首开始听,这张专辑封面也是我的微信头像hh。
在这里插入图片描述

标签:people,环形,约瑟夫,链表,环状,first,public,指针
来源: https://blog.csdn.net/qq_45852626/article/details/122474868