zoukankan      html  css  js  c++  java
  • 6、链表-单向环形链表

    来源:https://www.bilibili.com/video/BV1B4411H76f?p=28

    一、约瑟夫问题

    n个人围成一圈,从编号为k(1 ≤ k ≤ n)的那个人开始数,数到m的那个人出列。想要按照出列的顺序把这些人依次指出。

    这样的约瑟夫问题可以通过一个没有头结点的单向环形链表处理。结点数目为n,从第k个结点开始计数,按照链表顺序一直计数到m,将m对应的这个人删除。

    举一个n=5 k=1 m=2的例子

    二、思路

    2.1 创建单向环形链表的思路

    1、创建第一个结点,让first指针指向该节点,同时该节点与自身形成环

    2、后面需要知道当前创建的环形链表的大小,即上述约瑟夫问题中的n。将n个新结点加入到已有的环形链表中。

    2.2 遍历单向环形链表的思路

    1、辅助指针指向first结点

    2、利用while循环,直到【当前结点.next】=【first】

    2.3 约瑟夫问题的解决思路

    1、为了避免整个环形链表断掉,必须在要出去的结点前面加一个辅助结点helper,当前结点first出去之后,

    【first】=【first.next】

    【helper.next】=【first】

    2、因为整个问题开始的时候,first指向的是第一个创建的结点的位置,所以可以把helper之前它的前面,也就是环形的最后一个结点。当知道从编号为k的那个结点开始之后,两个指针同时后移相应的位置,令first指向开始的结点,helper指向前一个。

    3、开始报数之后,也是first、helper指针同时移动,直到走到要删除的结点。

    三、实现

    3.1 创建单向环形链表

    1、新建一个对应于约瑟夫问题的结点类,编号属性代表的是第几个人,next属性指向下一个人。

     1 public class Node3 {
     2     private int no;
     3     private Node3 next;
     4 
     5     public Node3(int no) {
     6         this.no = no;
     7     }
     8 
     9     public int getNo() {
    10         return no;
    11     }
    12 
    13     public void setNo(int no) {
    14         this.no = no;
    15     }
    16 
    17     public Node3 getNext() {
    18         return next;
    19     }
    20 
    21     public void setNext(Node3 next) {
    22         this.next = next;
    23     }
    24 }

     2、创建一个单向环形链表类CircleSingleLinkedList,类中有一个first结点,代表要创建的链表的第一个结点。在类中加入了利用输入的结点个数构建环形链表的方法,以及通过遍历的方式展示整个环形链表的方法。

     1 public class CircleSingleLinkedList {
     2     //创建第一个结点
     3     private Node3 first = new Node3(-1);
     4 
     5     //构建环形链表
     6     //为了简化,结点中只有编号这个属性,针对约瑟夫问题,编号顺序递增排列,但是需要知道有多少个人nums围成一圈
     7     public void addNode(int nums){
     8         if(nums < 1){
     9             System.out.println("参数nums错误");
    10             return;
    11         }
    12         Node3 curNode = null;//辅助指针,指向当前结点
    13         for (int i = 1; i <= nums; i++) {
    14             Node3 node3 = new Node3(i);
    15 
    16             if(i == 1){
    17                 //第一个结点,需要利用first自己形成环形
    18                 //之后first就不再动了,由curNode辅助指针完成之后的操作
    19                 first = node3;
    20                 first.setNext(first);
    21                 curNode = first;
    22             }else {
    23                 curNode.setNext(node3);
    24                 node3.setNext(first);
    25                 curNode = node3;
    26             }
    27         }
    28     }
    29 
    30     //遍历,显示环形链表
    31     public void show(){
    32         if(first.getNo() == -1){
    33             System.out.println("链表为空");
    34             return;
    35         }
    36         Node3 curNode = first;
    37         while (true){
    38             System.out.printf("当前结点的编号为%d",curNode.getNo());
    39             System.out.println();
    40             if(curNode.getNext() == first){
    41                 System.out.println("遍历完毕");
    42                 break;
    43             }
    44             curNode = curNode.getNext();
    45         }
    46     }
    47 }

    测试,这里加入了5个结点(5个人围成一圈),构成环形链表

     1     public static void main(String[] args) {
     2         CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
     3 
     4         circleSingleLinkedList.show();
     5         System.out.println();
     6 
     7         circleSingleLinkedList.addNode(5);
     8 
     9         circleSingleLinkedList.show();
    10 
    11     }

    结果

    链表为空
    
    当前结点的编号为1
    当前结点的编号为2
    当前结点的编号为3
    当前结点的编号为4
    当前结点的编号为5
    遍历完毕

    3.2 约瑟夫问题

    在CircleSingleLinkedList类中加入一个统计出圈顺序的方法

     1     //约瑟夫问题,
     2     // 根据用户输入的
     3     // **围成一圈的人的数目nums,
     4     // **从编号startNo开始,
     5     // **数到m的那个人出圈
     6     // 排列出圈的顺序
     7     public void countNode(int nums, int startNo, int m){
     8         if(first.getNo() == -1 || startNo < 1 || startNo > nums){
     9             System.out.println("参数错误");
    10             return;
    11         }
    12 
    13         Node3 helper = first;//辅助指针
    14         //令辅助指针指向first的前一个位置
    15         while (true){
    16             if(helper.getNext() == first){
    17                 break;
    18             }
    19             helper = helper.getNext();
    20         }
    21 
    22         //将first和helper定位到开始的位置startNo
    23         for (int i = 0; i < startNo - 1; i++) {
    24             first = first.getNext();
    25             helper = helper.getNext();
    26         }
    27 
    28         //开始报数
    29         while (true){
    30             //循环结束的标志是:只剩最后一个人了,first和helper指针都在他上面
    31             if(first == helper){
    32                 break;
    33             }
    34             //数到m
    35             for (int i = 0; i < m - 1; i++) {
    36                 first = first.getNext();
    37                 helper = helper.getNext();
    38             }
    39             //first指向的第m个人要出圈
    40             System.out.printf("%d出圈",first.getNo());
    41             System.out.println();
    42             first = first.getNext();//出圈时,first要重新定位
    43             helper.setNext(first);//helper的下一个也要重新指定
    44         }
    45         //输出一下最后剩的那个结点
    46         System.out.printf("最后一个是%d",first.getNo());
    47     }

    测试,在上面测试的基础上进行(已经插入了5个结点)

    1         circleSingleLinkedList.countNode(5,1,2);

    结果,与上面图中的顺序完全一致

    2出圈
    4出圈
    1出圈
    5出圈
    最后一个是3
  • 相关阅读:
    java去掉List中的重复值代码
    jquery 请求jsp传递json数据的方法
    jsp自定义标签分析
    jquery mouseout事件冒泡解决方法
    java split函数 对空的处理
    log4j使用感受
    mysql数据库主外键级联删除脚本RESTRICT --> CASCADE
    jquery 实现层级下拉框联动效果 代码
    JSP图片上传 公共工具类
    Apache和Nginx的对比
  • 原文地址:https://www.cnblogs.com/zhao-xin/p/13141790.html
Copyright © 2011-2022 走看看