单向环形链表介绍
- Josephu(约瑟夫、约瑟夫环)问题为
设编号为1,2,3,……n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
- 提示
用一个不带头节点的环形链表来处理 Josephu 问题:先构成一个有n个节点的单循环链表,然后由k节点起从1开始计数,记到m时,对应节点从链表中删除,然后再从被删除节点又从1开始计数,直到最后一个节点从链表中删除算法结束。
- Josephu 问题的示意图
- 约瑟夫问题-创建环形链表的思路图解
构建一个单向的环形链表思路
1. 先创建第一个节点, 让 first 指向该节点,并形成环形
2. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可
遍历环形链表
1. 先让一个辅助指针(变量) curBoy,指向first节点
2. 然后通过一个while循环遍历 该环形链表即可 curBoy.next == first 结束
-
约瑟夫问题-小孩出圈的思路分析图
根据用户的输入,生成一个小孩出圈的顺序
n = 5 , 即有5个人
k = 1, 从第一个人开始报数
m = 2, 数2下
1. 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.
补充: 小孩报数前,先让 first 和 helper 移动 k - 1次
2. 当小孩报数时,让first 和 helper 指针同时 的移动 m - 1 次
3. 这时就可以将first 指向的小孩节点 出圈
first = first .next
helper.next = first
原来first 指向的节点就没有任何引用,就会被回收
出圈的顺序 2->4->1->5->3
-
约瑟夫问题的代码实现
1 package com.jyj.linkedList; 2 3 public class Josephu { 4 public static void main(String[] args) { 5 CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); 6 circleSingleLinkedList.addBoy(5); 7 circleSingleLinkedList.showBoy(); 8 //测试小孩出圈顺序 9 circleSingleLinkedList.countBoy(1, 2, 5); 10 } 11 } 12 13 //创建一个环形的单向链表 14 class CircleSingleLinkedList { 15 //创建一个first节点,当前没有编号 16 private Boy first = new Boy(-1); 17 18 //添加小孩节点,构成环形链表 19 public void addBoy(int nums) { 20 //nums 做一个数据校验 21 if(nums < 1) { 22 System.out.println("nums 值不正确"); 23 return; 24 } 25 Boy curBoy = null; //辅助指针,帮助构建环形链表 26 //使用for来创建我们的环形链表 27 for(int i = 1;i <= nums;i++) { 28 //根据编号,创建小孩节点 29 Boy boy = new Boy(i); 30 //如果是第一个小孩 31 if(i == 1) { 32 first = boy; 33 first.setNext(first); 34 curBoy = first; 35 } else { 36 curBoy.setNext(boy); 37 boy.setNext(first); 38 curBoy = boy; 39 } 40 } 41 } 42 43 //遍历当前的单向环形链表 44 public void showBoy() { 45 //判空 46 if(first == null) { 47 System.out.println("链表为空,没有任何小孩~"); 48 return; 49 } 50 //因为first不能动,定义辅助指针 51 Boy curBoy = first; 52 while(true) { 53 System.out.printf("小孩的编号 %d ", curBoy.getNo()); 54 if(curBoy.getNext() == first) { //遍历完毕 55 break; 56 } 57 curBoy = curBoy.getNext(); //curBoy后移 58 } 59 } 60 61 //根据用户输入,计算小孩出圈顺序 62 /** 63 * 64 * @param startNo 表示从第几个小孩开始数数 65 * @param countNo 表示数几下 66 * @param nums 表示最初有多少小孩在圈中 67 */ 68 public void countBoy(int startNo,int countNo,int nums) { 69 //数据校验 70 if(first == null || startNo < 1 || startNo > nums) { 71 System.out.println("参数不正,请重新输入"); 72 return; 73 } 74 //创建辅助指针(变量)helper,事先应该指向环形链表的最后这个节点。 75 Boy helper = first; 76 while(true) { 77 if(helper.getNext() == first) {//helper 是最后一个节点 78 break; 79 } 80 helper = helper.getNext(); 81 } 82 //小孩报数前,先让first 和 helper移动 startNo-1 次 83 for(int i = 0;i < startNo - 1;i++) { 84 first = first.getNext(); 85 helper = helper.getNext(); 86 } 87 while(true) { 88 if(first == helper) {//剩最后一个节点 89 break; 90 } 91 //小孩报数时,让first 和 helper指针同时的移动 countNo - 1 次 92 for(int i = 0;i < countNo - 1;i++) { 93 first = first.getNext(); 94 helper = helper.getNext(); 95 } 96 System.out.printf("小孩编号 %d 出圈 ", first.getNo()); 97 first = first.getNext(); 98 helper.setNext(first); 99 } 100 System.out.printf("最后一个小孩的编号是first : %d,helper : %d ", first.getNo(),helper.getNo()); 101 } 102 } 103 104 //创建一个Boy类,表示一个节点 105 class Boy { 106 private int no;//编号 107 private Boy next;//指向下一个节点,默认为null 108 public Boy(int no) { 109 this.no = no; 110 } 111 public int getNo() { 112 return no; 113 } 114 public void setNo(int no) { 115 this.no = no; 116 } 117 public Boy getNext() { 118 return next; 119 } 120 public void setNext(Boy next) { 121 this.next = next; 122 } 123 }
以上