概念介绍
有同学想了解约瑟夫问题,今天它来了!直接进入主题,什么是约瑟夫问题?约瑟夫问题:N个人围成一圈,从约定编号为K的人开始报数,第M个将被杀掉,依次类推,最后剩下一个,其余人都将被杀掉。
直接上图展示,初始化状态: 假设n=6,总共有6个人,k=1,从第一个人开始报数,m=5,每次数五个。
第一次报数:从一号开始,数五个数,1-2-3-4-5,数完五个数,五号被杀死,第一次报数后,剩余人数如下。
第二次报数: 从被杀死的五号的下一位开始报数,也就是六号,数五个数,6-1-2-3-4,数数完毕,四号被杀死,第二次报数后,剩余人数如下。
第三次报数: 从被杀死的四号的下一位开始报数,同样是六号,数五个数,6-1-2-3-6,数数完毕,六号被杀死,第三次报数后,剩余人数如下。
第四次报数: 从被杀死的六号的下一位开始报数,也就是一号,数五个数,1-2-3-1-2,数数完毕,二号被杀死,第四次报数后,剩余人数如下。
第五次报数: 从被杀死的二号的下一位开始报数,也就是三号,数五个数,3-1-3-1-3,数数完毕,三号被杀死,只剩下一号,Game Over!
代码实现
实现思路:用单向循环链表来表示圈,将人杀死后修改链表上的节点即可。
第一步:构建节点上对象。
1 class Person { 2 // 人的编号 3 private int no; 4 // 指向下一个人 5 private Person next; 6 7 public Person(int no) { 8 this.no = no; 9 } 10 11 public int getNo() { 12 return no; 13 } 14 15 public void setNo(int no) { 16 this.no = no; 17 } 18 19 public Person getNext() { 20 return next; 21 } 22 23 public void setNext(Person next) { 24 this.next = next; 25 } 26 27 }
第二步:创建一个环形的单向链表,将带有编号的人组织起来。
class CircleSingleLinkedList { // 创建一个first节点,当前没有编号 private Person first = null; // 往环形链表中添加人,persons表示添加的人数 public void addPerson(int persons) { if (persons < 1) { System.out.println("persons的值异常"); return; } // 辅助指针,帮助构建环形链表 Person curPerson = null; // 往环形链表中添加人 for (int i = 1; i <= persons; i++) { Person person = new Person(i); // 如果是第一次添加人 if (i == 1) { first = person; first.setNext(first); curPerson = first; } else { curPerson.setNext(person); person.setNext(first); curPerson = person; } } } }
第三步:实现数数的方法。
/** * * @param startNo * 表示从第几个人开始数数 * @param countNum * 表示每次数几下 * @param nums * 表示圈中的人数 */ public void startCount(int startNo, int countNum, int nums) { if (first == null || startNo < 1 || startNo > nums) { System.out.println("参数输入有误, 请重新输入"); return; } // 辅助指针,当我们要退出时的依据,当helper == first时,退出 Person helper = first; // 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点 while (true) { if (helper.getNext() == first) { break; } helper = helper.getNext(); } // 报数开始前,先让first和helper移动 k - 1次 for(int j = 0; j < startNo - 1; j++) { first = first.getNext(); helper = helper.getNext(); } // 报数开始时,让first和helper指针同时 的移动 m - 1 次, 然后first指向人被杀死,出圈 // 这里是一个循环操作,直至圈中只有一个节点 while(true) { // 当helper == first时,说明圈中只有一个节点 if(helper == first) { break; } // 让 first 和 helper 指针同时移动 countNum - 1 for(int j = 0; j < countNum - 1; j++) { first = first.getNext(); helper = helper.getNext(); } System.out.printf("第%d号人被杀死\n", first.getNo()); // 出圈操作 first = first.getNext(); helper.setNext(first); } System.out.printf("最后留在圈中的人的编号是:%d \n", first.getNo()); }
至此,代码编写完成,Git地址:https://github.com/HollowCup/algorithms-and-data-structure,具体实现位于algorithms工程下的josephus目录,如果发现不足之处,请联系我进行更改,十分感谢!关注我,为你揭秘更多算法!