• 数据结构:单向环形链表


    单向环形链表

    目录:

     1、简介

     2、简单实用

          1、创建节点

        2、创建链表对象

        3、是否为空的方法

            4、增加一个节点到尾部的方法

        5、增加一个节点并保持从小到大的顺序

        6、增加指定个数的节点

       3、约瑟夫问题

     4、约瑟夫问题使用环形单链表解决

     5、本随笔中所有源码

     1、简介

    环形单链表和普通单链表几乎一样,唯一不同的就是普通单链表的最后一个节点的next为空,而我们的环形单链表的最后一个节点的net为头节点,形成一个闭环。

    如上图,第一个节点也就是头结点,第五个节点就是最后一个节点,但是在环形单链表中next会指向第一个节点,而普通单链表中最后一个节点的next是为空。

    2、简单使用

    1):创建节点对象

     1 /*节点*/
     2 class Node {
     3     private int no;
     4     private Node next;
     5 
     6     public Node(int no) {
     7         this.no = no;
     8     }
     9 
    10     public int getNo() {
    11         return no;
    12     }
    13 
    14     public void setNo(int no) {
    15         this.no = no;
    16     }
    17 
    18     public Node getNext() {
    19         return next;
    20     }
    21 
    22     public void setNext(Node next) {
    23         this.next = next;
    24     }
    25 
    26     @Override
    27     public String toString() {
    28         return "Node{" +
    29                 "no=" + no +
    30                 '}';
    31     }
    32 }
    节点对象

    2):创建链表对象 

    1 /*单向环形链表*/
    2 class CircleSingleLinkedList {
    3     private Node first = null;
    4 }
    链表对象

    3):是否为空的方法

    1  /*是否为空的方法*/
    2     public boolean isEmpty() {
    3         return first == null;
    4     }
    是否为空的方法(放置在CircleSingleLinkedList中)

    如果first为空的话,那就肯定是空了。

    4):增加一个节点到尾部的方法

     1  /*增加一个节点到尾部的方法*/
     2     public boolean addNode(Node node) {
     3         /*如果头为空,那么就将头赋值,并且将头的next指向自己,达到环形的效果*/
     4         if (isEmpty()) {
     5             first = node;
     6             first.setNext(first);
     7             return true;
     8         }
     9 
    10         /*辅助节点*/
    11         Node tmp = first;
    12         while (true) {
    13             /*如果当前节点的next为头节点,那么说明它就是尾部节点了。*/
    14             if (tmp.getNext() == first) {
    15                 /*那么当前节点的next就等于我们新添加的节点*/
    16                 tmp.setNext(node);
    17                 /*既然新添加的节点是在最后面,那么新添加的节点的next就是头节点*/
    18                 node.setNext(first);
    19                 return true;
    20             }
    21             /**/
    22             tmp = tmp.getNext();
    23         }
    24     }
    增加一个节点到尾部的方法(放置在CircleSingleLinkedList中)

     如果当前链表为空,那么直接把node赋值给我们链表的头节点。

    否则就遍历,然后遍历到最后一个条件的条件是当前节点的下一个节点等于头节点,那么就说明当前节点就是最后一个节点了。

    然后找到最后一个节点后,最后一个节点的next等于我们当前加入的节点,需要注意的是当前加入的节点的next要为头节点。

     

    注意:这里遍历到尾部的条件和加入节点后尾部节点的next处理和普通单链表不一致。因为要形成闭环,那么尾部节点的next一定要为first,否则就闭不了。

    5):添加一个节点并保持从小到大的顺序

     1  /*增加一个节点并保持从小到大顺序方法*/
     2     public boolean addNodeByOrder(Node node) {
     3         /*如果头为空,那么就将头赋值,并且将头的next指向自己,达到环形的效果*/
     4         if (isEmpty()) {
     5             first = node;
     6             first.setNext(first);
     7             return true;
     8         }
     9         /*判断头结点是否比我们需要添加的节点大,如果是那么就得置换first*/
    10         if (first.getNo() > node.getNo()) {
    11             node.setNext(first);
    12             Node tem = first;
    13             first = node;
    14             /*
    15                 设置完成后,因为原最后一个节点指向的并不是我们新的头,而是旧头,所以我们需要在进行更改
    16             */
    17             Node tmp2 = tem;
    18             while (true) {
    19                 if (tmp2.getNext() == tem) {
    20                     tmp2.setNext(node);
    21                     break;
    22                 }
    23                 tmp2 = tmp2.getNext();
    24             }
    25             return true;
    26         }
    27         /*辅助节点*/
    28         Node tmp = first;
    29         while (true) {
    30             /*当前节点的next大于node,那么就把node放置到当前节点的next中*/
    31             if (tmp.getNext().getNo() > node.getNo()) {
    32                 node.setNext(tmp.getNext());
    33                 tmp.setNext(node);
    34                 return true;
    35             }
    36             /*判断是否到达尾部*/
    37             else if (tmp.getNext() == first) {
    38                 tmp.setNext(node);
    39                 node.setNext(first);
    40                 return true;
    41             }
    42             tmp = tmp.getNext();
    43         }
    44     }
    添加一个节点并保持从小到大顺序(放置在CircleSingleLinkedList中)

     这个要稍微麻烦一点,

      1、判断头是否为空,如果为空,那么就将加入的这个节点赋值给头,然后停止

      2、然后判断一种特殊情况,因为我们的环形单链表中头节点是存储数据的,那么判断头节点是否比我们新添加的node大,如果是那么需要互换位置

        注意:如果这种特殊情况成立,那我们置换了first和node的位置以后,链表的尾部其实还是指向的原first,我们需要遍历找到链表的尾部,找到最后一个节点,然后最后一个节点的next等于我们新添加的node。

      3、如果以上情况都不成立,那么就遍历单链表,例如当前遍历到的节点为current,然后我们需要添加的节点为node

        那么如果current的next的no大于node的no,那么我们我们就需要让current的next为node,然后current的原next就为node的next。

        这里不明白的,可以去看看单链表的添加,那个有图,这种情况添加和普通单链表没有区别,所以图我就不再画了,【单链表传送门】

    6):增加指定个数的节点

     1  /*增加指定数量个节点*/
     2     public boolean addNodeByCount(int count) {
     3         /*准备添加的节点的no*/
     4         int startNo = -1;
     5         /*如果链表为空,那么默认添加一个头节点,那么既然这里添加了一个头节点,那么startNo要改变,我们后面创建节点的次数也要改变*/
     6         if (first == null) {
     7             first = new Node(1);
     8             first.setNext(first);
     9             count--;
    10             startNo = 2;
    11         } else {
    12             /*否则就找到尾部节点的no然后+1*/
    13             Node temp = first;
    14             while (true) {
    15                 if (temp.getNext() == first) {
    16                     startNo = temp.getNo() + 1;
    17                     break;
    18                 } else {
    19                     temp = temp.getNext();
    20                 }
    21             }
    22         }
    23         /*再循环加入*/
    24         for (int i = 0; i < count; i++) {
    25             Node node = new Node(startNo);
    26             addNodeByOrder(node);
    27             startNo++;
    28         }
    29         return true;
    30     }
    增加指定个数的节点(放置在CircleSingleLinkedList中)

       1、首先我们添加节点的时候,要知道我们肯定要保持顺序对吧,那么我们增加指定个数的节点那我们添加的节点肯定要比原有链表中的链表的no要大

       2、我们定义一个变量startNo来存储最后一个节点的no+1,为什么要+1呢,因为我们后面会直接用这个no进行创建节点。

       3、如果链表为空,那么我们直接创建一个节点赋值给first,然后不要忘记设置first的next为自己,然后因为我们first这里这里创建了一次,所以count要--,然后startNo要=2(因为first的no是1嘛)

       4、如果链表不为空,那么我们就需要找到最后一个节点的no,然后赋值给startNo

       5、然后循环的调用addNodeByCount方法添加节点。

    3、约瑟夫问题

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

     

    可以稍微换个说法,数量为n个小孩围成一圈,从第k个小孩开始报数,数到m的那个人出列,然后让产生一个所有小孩出列的顺序。

    4、约瑟夫问题是用环形单链表解决

     我们假设总共有五个娃,从第二个人开始数,每次数二次,然后最后剩一个人,我们来看看大概是个啥样。

      那么

        n = 5

        k = 2

        m = 2

      目录:

        1、首先刚开始,五个娃

        2、移动到k个位置

        3、然后进行第一次数数,3出列

        4、继续出列,出列5

        5、继续出列,出列2

        6、继续出列,出列1

        7、出列最后一个,4

    1) 首先刚开始,五个娃:

    这就是最开始的样子,first代表链表的头部,end代表链表的尾部

    2)移动到k的位置

    移动到k的位置我们只需要将first和end同时移动k-1次,因为当前first就是在1撒,如果k为2,那么就是移动2-1次。

    3)然后进行第一次数数,3出列

    注意,first本身也算一下哦,然后我们移动m下,m为2,那么就代表着数2下,然后移动1次,也就是m-1。

    移动一次以后,我们就找到了第一个需要出列的娃,也就是将first指向的那个一个节点出列,那么既然它出列了,那么first就要指向出列的那个节点的next了。

    出列后就成了这个样子,那么第一个出列的就是3

    注意:原先end节点的next是指向的3,我们需要重新的给end节点的end赋值为first

    4)继续出列,出列5

    按照上面的法子,继续数数就成了这个样子:

     然后把first指向的节点出列

    然后第二次出列的就是5。

    5)继续出列,出列2

    还是进行数数:

    然后再把first指向的节点出列。

     那么这次出列的就是2

     6)继续出列,出列1

    还是先数数:

     然后让first指向的节点出列,就变成了这样 

    7)出列最后一个,4

    然后就出列完成,总共的顺序就是3 5 2 1 4。

     5、本随笔中所有源码

      1 package t4;
      2 
      3 import t2.Test;
      4 
      5 /**
      6  * @author 自在仙
      7  * @create 2020年04月19  21:44
      8  */
      9 public class CircleSingleLinkedListDemo {
     10 
     11     public static void main(String[] args) {
     12        /* Node node1 = new Node(1);
     13         Node node2 = new Node(2);
     14         Node node3 = new Node(3);
     15         CircleSingleLinkedList list = new CircleSingleLinkedList();
     16         list.addNodeByOrder(node3);
     17         list.addNodeByOrder(node2);
     18         list.addNodeByOrder(new Node(100));
     19         list.addNodeByCount(5);*/
     20 //        list.addNodeByOrder(node1);
     21 //        list.addNodeByOrder(new Node(0));
     22 //        list.display();
     23 //        list.addNodeByCount(1);
     24 
     25         CircleSingleLinkedList list = new CircleSingleLinkedList();
     26         list.addNodeByCount(5);
     27         list.joseph(2,2,2);
     28 
     29 
     30     }
     31 
     32 
     33 }
     34 
     35 /*单向环形链表*/
     36 class CircleSingleLinkedList {
     37     private Node first = null;
     38 
     39     /*是否为空的方法*/
     40     public boolean isEmpty() {
     41         return first == null;
     42     }
     43 
     44     /*增加一个节点到尾部的方法*/
     45     public boolean addNode(Node node) {
     46         /*如果头为空,那么就将头赋值,并且将头的next指向自己,达到环形的效果*/
     47         if (isEmpty()) {
     48             first = node;
     49             first.setNext(first);
     50             return true;
     51         }
     52 
     53         /*辅助节点*/
     54         Node tmp = first;
     55         while (true) {
     56             /*如果当前节点的next为头节点,那么说明它就是尾部节点了。*/
     57             if (tmp.getNext() == first) {
     58                 /*那么当前节点的next就等于我们新添加的节点*/
     59                 tmp.setNext(node);
     60                 /*既然新添加的节点是在最后面,那么新添加的节点的next就是头节点*/
     61                 node.setNext(first);
     62                 return true;
     63             }
     64             /**/
     65             tmp = tmp.getNext();
     66         }
     67     }
     68 
     69     /*增加一个节点并保持从小到大顺序方法*/
     70     public boolean addNodeByOrder(Node node) {
     71         /*如果头为空,那么就将头赋值,并且将头的next指向自己,达到环形的效果*/
     72         if (isEmpty()) {
     73             first = node;
     74             first.setNext(first);
     75             return true;
     76         }
     77         /*判断头结点是否比我们需要添加的节点大,如果是那么就得置换first*/
     78         if (first.getNo() > node.getNo()) {
     79             node.setNext(first);
     80             Node tem = first;
     81             first = node;
     82             /*
     83                 设置完成后,因为原最后一个节点指向的并不是我们新的头,而是旧头,所以我们需要在进行更改
     84             */
     85             Node tmp2 = tem;
     86             while (true) {
     87                 if (tmp2.getNext() == tem) {
     88                     tmp2.setNext(node);
     89                     break;
     90                 }
     91                 tmp2 = tmp2.getNext();
     92             }
     93             return true;
     94         }
     95         /*辅助节点*/
     96         Node tmp = first;
     97         while (true) {
     98             /*当前节点的next大于node,那么就把node放置到当前节点的next中*/
     99             if (tmp.getNext().getNo() > node.getNo()) {
    100                 node.setNext(tmp.getNext());
    101                 tmp.setNext(node);
    102                 return true;
    103             }
    104             /*判断是否到达尾部*/
    105             else if (tmp.getNext() == first) {
    106                 tmp.setNext(node);
    107                 node.setNext(first);
    108                 return true;
    109             }
    110             tmp = tmp.getNext();
    111         }
    112     }
    113 
    114     /*增加指定数量个节点*/
    115     public boolean addNodeByCount(int count) {
    116         /*准备添加的节点的no*/
    117         int startNo = -1;
    118         /*如果链表为空,那么默认添加一个头节点,那么既然这里添加了一个头节点,那么startNo要改变,我们后面创建节点的次数也要改变*/
    119         if (first == null) {
    120             first = new Node(1);
    121             first.setNext(first);
    122             count--;
    123             startNo = 2;
    124         } else {
    125             /*否则就找到尾部节点的no然后+1*/
    126             Node temp = first;
    127             while (true) {
    128                 if (temp.getNext() == first) {
    129                     startNo = temp.getNo() + 1;
    130                     break;
    131                 } else {
    132                     temp = temp.getNext();
    133                 }
    134             }
    135         }
    136         /*再循环加入*/
    137         for (int i = 0; i < count; i++) {
    138             Node node = new Node(startNo);
    139             addNodeByOrder(node);
    140             startNo++;
    141         }
    142         return true;
    143     }
    144 
    145     /**
    146      * @param n 总人数
    147      * @param k 第k个人开始
    148      * @param m 每次数多少个
    149      */
    150     public void joseph(int n, int k, int m) {
    151         /*
    152          * 1、先判断是否合法
    153          * 2、找到尾部节点
    154          * 3、从第k个人开始撒,刚开始first是1,那么我们要后移k-1次,end和first都要移动
    155          * 4、死循环,循环内进行移动位置,每移动一次,就移出去一次,只要first和end一样的时候,我们就停止循环。
    156          * */
    157         if (n < 2 || k > n) {
    158             /*不合法*/
    159             throw new RuntimeException("数据不合法,n must > 2,k must < n");
    160         }
    161         /*找到尾部节点*/
    162         Node end = first;
    163         while (true) {
    164             if (end.getNext() == first) {
    165                 break;
    166             }
    167             end = end.getNext();
    168         }
    169 
    170         /*开始移动到第k个人*/
    171         for (int i = 1; i < k; i++) {
    172             first = first.getNext();
    173             end = end.getNext();
    174         }
    175         /*开始移动*/
    176         while (true) {
    177             if (first == end) {
    178                 System.out.println(first.getNo());
    179                 break;
    180             }
    181             /*移动m-1次*/
    182             for (int i = 1; i < m; i++) {
    183                 /*移动*/
    184                 first = first.getNext();
    185                 /*end也需要移动*/
    186                 end = end.getNext();
    187             }
    188             System.out.println(first.getNo());
    189             /*移动完成以后,删除first,end一栋一次*/
    190             /*first*/
    191             first = first.getNext();
    192             /*给end设置next为first*/
    193             end.setNext(first);
    194         }
    195 
    196     }
    197 
    198     /*展示链表中的数据*/
    199     public void display() {
    200         if (isEmpty()) {
    201             throw new RuntimeException("链表为空");
    202         }
    203         Node tmp = first;
    204         while (true) {
    205             System.out.println(tmp);
    206             if (tmp.getNext() == first) {
    207                 break;
    208             }
    209             tmp = tmp.getNext();
    210         }
    211     }
    212 
    213 }
    214 
    215 /*节点*/
    216 class Node {
    217     private int no;
    218     private Node next;
    219 
    220     public Node(int no) {
    221         this.no = no;
    222     }
    223 
    224     public int getNo() {
    225         return no;
    226     }
    227 
    228     public void setNo(int no) {
    229         this.no = no;
    230     }
    231 
    232     public Node getNext() {
    233         return next;
    234     }
    235 
    236     public void setNext(Node next) {
    237         this.next = next;
    238     }
    239 
    240     @Override
    241     public String toString() {
    242         return "Node{" +
    243                 "no=" + no +
    244                 '}';
    245     }
    246 }
    本随笔中的所有源码

                    ps:学艺不精,表达不明,还望海涵。

  • 相关阅读:
    centos软件安装
    新手根据菜鸟教程安装docker,从No package docker-io available开始遇到的坑...
    性能基准测试:KVM大战Xen
    Netdata---Linux系统性能实时监控平台部署记录
    Linux Storage Stack Diagram存储堆栈图
    /proc目录下文件详解
    磁盘IO计算
    RPM包下载网址
    搜索框获取转移焦点事件
    用点击事件做红绿灯2
  • 原文地址:https://www.cnblogs.com/daihang2366/p/12744989.html
Copyright © 2020-2023  润新知