一、约瑟夫问题介绍
Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数 到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由 此产生一个出队编号的序列。
1.1 约瑟夫问题示意图
1.2 使用不带头节点的环形单链表解决思路
用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除,算法结束。
1.2.1 创建环形链表思路
1.2.2 小孩出圈思路分析
二、编写Java代码解决问题
2.1 编写Boy类
1 /** 2 * 创建一个Boy类,表示一个节点 3 */ 4 public class Boy { 5 6 private int no; // 编号 7 private Boy next; // 指向下一个节点 8 9 public Boy(int no) { 10 this.no = no; 11 } 12 13 public int getNo() { 14 return no; 15 } 16 17 public void setNo(int no) { 18 this.no = no; 19 } 20 21 public Boy getNext() { 22 return next; 23 } 24 25 public void setNext(Boy next) { 26 this.next = next; 27 } 28 }
2.2 编写CircleSingleLinkedList类
1 /** 2 * 创建一个环形的单向链表 3 */ 4 public class CircleLinkedList { 5 // 创建一个first节点,当前没有编号 6 private Boy first = null; 7 // 添加小孩节点,构成一个环形的链表 8 public void addBoy(int nums) { 9 // nums 做一个数据校验 10 if(nums < 1) { 11 System.out.println("nums 的值不正确"); 12 return; 13 } 14 15 Boy curBoy = null; // 辅助指针,帮助构建环形链表 16 // 使用for来创建我们的环形链表 17 for(int i = 1; i <= nums; i++) { 18 // 根据编号,创建小孩节点 19 Boy boy = new Boy(i); 20 // 如果是第一个小孩 21 if(i == 1) { 22 first = boy; 23 first.setNext(boy); // 够成环 24 curBoy = boy; // 让curBoy指向第一个小孩 25 } else { 26 curBoy.setNext(boy); 27 boy.setNext(first); 28 curBoy = boy; 29 } 30 } 31 } 32 33 // 遍历当前的环形链表 34 public void showBoy(){ 35 // 判断链表是否为空 36 if (first == null) { 37 System.out.println("没有任何小孩~"); 38 return; 39 } 40 // 因为first不能动,因此我们仍然使用一个辅助指针完成遍历 41 Boy curBoy = first; 42 while (true) { 43 System.out.printf("小孩的编号%d ", curBoy.getNo()); 44 if(curBoy.getNext() == first) { 45 break; 46 } 47 curBoy = curBoy.getNext(); // curBoy后移 48 } 49 } 50 51 /** 52 * 根据用户的输入,计算出小孩出圈的顺序 53 * @param startNo 表示从第几个小孩开始数数 54 * @param countNum 表示数几下 55 * @param nums 表示最初优几个小孩在圈中 56 */ 57 public void out(int startNo, int countNum, int nums){ 58 59 // 先对数据进行校验 60 if (first == null || startNo < 1 || startNo > nums) 61 System.out.println("参数输入有误,请重新输入~~"); 62 // 移动first 63 for (int i = 1; i < startNo; i ++) { 64 first = first.getNext(); 65 } 66 67 Boy helper = first; 68 // helper 移动到最后 69 while (true) { 70 if(helper.getNext() == first) { 71 break; 72 } 73 helper = helper.getNext(); // helper后移 74 } 75 // 循环出人 76 while (true) { 77 for (int i = 1; i < countNum; i ++) { 78 helper = helper.getNext(); 79 } 80 // 出人 81 System.out.printf("小孩的编号%d出圈 ", helper.getNext().getNo()); 82 helper.setNext(helper.getNext().getNext()); 83 84 if(helper.getNext() == helper) { 85 System.out.printf("小孩的编号%d出圈 ", helper.getNext().getNo()); 86 first = null; 87 break; 88 } 89 } 90 } 91 }
2.3 编写测试类Josepfu
1 /** 2 * 约瑟夫问题介绍 3 * Josephu 问题为: 4 * 设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数, 5 * 数 到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推, 6 * 直到所有人出列为止,由 此产生一个出队编号的序列。 7 */ 8 public class Josephu { 9 public static void main(String[] args) { 10 // 测试 11 CircleLinkedList circleLinkedList = new CircleLinkedList(); 12 circleLinkedList.addBoy(5); 13 circleLinkedList.showBoy(); 14 15 System.out.println("==============="); 16 17 circleLinkedList.out(1,2,5); 18 } 19 }
原文链接:https://blog.csdn.net/a1786742005/article/details/104059837