zoukankan      html  css  js  c++  java
  • 1.6 单向环形链表和约瑟夫问题

    • 基本介绍

      • 单向循环链表和单向链表基本一样,尾节点的next指向第一个节点
      • 当链表中只有一个节点时,该节点的next指向本身
    • 约瑟夫问题

      • 基本介绍
        • 设编号为 1,2,… n 的n个人围坐一圈,约定编号为 k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列
        • 例如:编号为1,2,3,4,5 从1开始,数2次,则出队编号为2,4,1,5,3
      • 实现思路
        • 采用单向环形链表,将数到那个节点在环形链表中删除后,重新构建环形链表,直到剩下最后一个节点
        • 由于采用的单向链表,所以需要两个指针,current指向当前的节点(也就是要出队的节点,初始值为head节点),currentBefore指向当前节点的前一个节点(用来删除当前节点,初始值为head节点的前一个节点)
        • 将两个节点同时移动k-1次,第一次的报号位置,由于本身也要报号,所以k-1次(两个指针必须相连,且current在后)
        • 开始报号,将两个节点同时移动k-1次(原理同上)
        • 出队current = current.next; currentBefore = current;
        • 当current == currentBefore; 队列中就只剩下了一个节点(结束)
    • 代码实现

    • public class CircleSingleLinkedListDemo {
         public static void main(String[] args) {
             CircleSingleLinkedLis circleSingleLinkedLis = new CircleSingleLinkedLis();
             // 创建并显示单向环形链表
             circleSingleLinkedLis.create(10);
             circleSingleLinkedLis.show();
             // 实现约瑟夫问题
             System.out.println("---约瑟夫问题---");
             circleSingleLinkedLis.josephu(5, 10, circleSingleLinkedLis.size());
         }
      }
      // 单向环形链表类
      class CircleSingleLinkedLis {
      
         private Children head; // 头节点,将加入的第一个节点作为头节点(不能动,在约瑟夫环中可以动)
      
         // 创建单向环形链表(参数为有几个节点)
         public void create(int number) {
             if (number < 2) {
                 System.out.println("至少两个节点");
                 return;
             }
             Children current = null; // 尾指针 指向下一个节点为头节点的节点(方便插入,不需要每次都遍历)
             for (int i = 1; i <= number; i++) { // i作为节点的编号
                 Children children = new Children(i);
                 if (null == head) { // 第一个节点 自己指向自己 单独处理也是为了防止空指针
                     head = children;
                     children.next = head;
                     current = head;
                 } else { // 先将当前节点的下一个节点指向要添加的节点,再后移当前节点,再将当前节点的下一个节点指向头节点
                     current.next = children;
                     current = current.next;
                     children.next = head;
                 }
             }
         }
      
         // 显示单向环形链表
         public void show() {
             if (null == head) {
                 System.out.println("单向环形链表为空");
             }
             Children temp = head;
             while (null != head) {
                 System.out.println(temp);
                 temp = temp.next; // 后移节点
                 if (temp == head) {
                     break; // 到头节点了 退出
                 }
             }
         }
      
         // 获取链表节点个数
         public int size() {
             int sum = 0;
             Children temp = head;
             while (null != temp ) {
                 sum++;
                 temp = temp.next;
                 if (temp == head) {
                     break;
                 }
             }
             return sum;
         }
         
         /**
          * 约瑟夫问题实现(一群小孩围在一起,由第k个小孩报n下数,停止报数的那个小孩出圈,问小孩出圈的顺序)
          * 采用两个指针(一个指向当前报数的孩子,另一个指向报数孩子的前一个孩子,因为单链表删除必须拿到当前节点的前一个节点)
          * @param start 开始报数的节点
          * @param count 报多少下
          * @param size 总共有多少个小孩(此处不能大于链表的有效个数)
          */
         public void josephu(int start, int count, int size) {
             if (null == head || start < 1 || start > size || size > this.size()) {
                 System.out.println("输入的数据不合法");
                 return;
             }
             Children current = head; // 当前节点
             Children currentBefore = current; // 当前节点的前一个节点
             while (currentBefore.next != head) { // 寻找当前节点的前一个节点
                 currentBefore = currentBefore.next; // 指针后移
             }
             for (int i = 0; i < start - 1; i++) { // 将两个指针移动到起始位置 例 1 -> 3 需移动2次 则是 start - 1
                 current = current.next;
                 currentBefore = currentBefore.next;
             }
             while (current != currentBefore) { // 如果圈中只剩一个小孩 则两个指针重合
                 // 从起始位置报数 由于起始节点本身也要报数 则是移动 count - 1
                 for (int i = 0; i < count - 1; i++) {
                     current = current.next;
                     currentBefore = currentBefore.next;
                 }
                 // 报数完毕 移除当前节点 并重新构建链表
                 System.out.println(current);
                 current = current.next; // 后移当前指针
                 currentBefore.next = current; // 重新构建链表
             }
             System.out.println(current); // 输出圈中最后一个节点的信息
         }
      
      }
      // 链表节点类
      class Children {
         public int no;
         public Children next; // 指向下一个节点
      
         public Children(int no) {
             this.no = no;
         }
      
         @Override
         public String toString() {
             return "Children{" +
                     "no=" + no +
                     '}';
         }
      }
      
  • 相关阅读:
    ###第五次作业###
    第四次作业
    第三次作业
    jquery cookie插件
    jquery.form.js(ajax表单提交)
    jquery 中 $.map 用法
    jQuery中的$.grep()使用
    jquery-validation验证插件
    软件工程实践2017第一次作业
    jQuery UI dialog 的使用
  • 原文地址:https://www.cnblogs.com/xiaokantianse/p/13579573.html
Copyright © 2011-2022 走看看