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
  • 相关阅读:
    现代软件工程 第一章 概论 第3题——韩婧
    现代软件工程 第一章 概论 第2题——韩婧
    小组成员邓琨、白文俊、张星星、韩婧
    UVa 10892 LCM的个数 (GCD和LCM 质因数分解)
    UVa 10780 幂和阶乘 求n!中某个因子的个数
    UVa 11859 除法游戏(Nim游戏,质因子)
    Codeforces 703C Chris and Road 二分、思考
    Codeforces 703D Mishka and Interesting sum 树状数组
    hdu 5795 A Simple Nim SG函数(多校)
    hdu 5793 A Boring Question 推公式(多校)
  • 原文地址:https://www.cnblogs.com/zhao-xin/p/13141790.html
Copyright © 2011-2022 走看看