约瑟夫问题以及环形链表的解决方案
约瑟夫问题介绍:
Josephu问题为: 设编号为1, 2, …… n的n个人围坐一圈 约定编号为k (1<=k<=n) 的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。 n=5,即有5个人 k=1,从第一个人开始报数 m=2,数2下
提示
用一个不带头结点的循环链表来处理Josephu问题:先构成一个有n个结点的单循环链表
然后由k结点起从1开始计数,计到m时,对应结点从链表中删除。
然后再从被删除结点
的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
思路:
构建一个单向的环形链表思路: 1.先创建第一个节点让first指向该节点,并形成环形 2.后面当我们每创建一个新的节点, 就把该节点,加入到已有的环形链表中即可. 遍历环形列表: 1.先让一个辅助指针(变量curBoy,指向first节点) 2.然后通过一个while循环遍历 该环形链表即可curBoy.next == first结束
添加小朋友代码实现:
/** * 添加小孩的节点 * nums:参与游戏的小孩个数 */ public void addBoy(int nums){ if (nums < 1){ System.out.println("您输入的数据有误!"); return; } Boy curBoy = null;//辅助指针 for (int i=1;i<=nums;i++){ Boy boy = new Boy(i);//根据编号创建小孩节点 if (i == 1){ first = boy; first.setNext(first);//构成环状 curBoy = first;//让辅助指针指向第一个节点 }else{ curBoy.setNext(boy);//从第二个小朋友开始,让辅助指针的下一个节点 指向 新的节点 boy.setNext(first);//再让第二个小朋友的下一个节点 指向 第一个小朋友 curBoy = boy;//让curBoy指向boy,也就是辅助节点向后移 } } }
遍历链表代码实现:
/** * 遍历环形链表 */ public void Traverse(){ //判断链表是否为空 if (first == null){ System.out.println("链表为空!"); return; } //因为first不能动,所以在这定义辅助指针 Boy curBoy = first; while (true){ System.out.println("小朋友的编号为:"+curBoy.getNo()); if (curBoy.getNext() == first)//说明已经遍历完毕 break; curBoy = curBoy.getNext();//让curBoy后移 } }
小朋友出圈顺序分析:
小朋友出圈代码实现:
/** * 根据用户的输入,计算出小朋友出圈的顺序 * @param startNo :表示从哪个小朋友开始报数 * @param countNum:表示数几下 * @param nums:表示最初有多少小朋友参与游戏 */ public void countBoy(int startNo,int countNum,int nums){ if (first == null || startNo < 0 || startNo > nums){ System.out.println("参数输入有误!"); return; } //创建辅助节点,帮助小朋友出圈 Boy temp = first; while (true){ if (temp.getNext() == first)//辅助指针temp事先应该指向节点的最后一个位置 break; temp = temp.getNext();//向后移动,找到最后链表的最后一个位置 } //移动小朋友到指定位置上面 for (int j = 0;j<startNo-1;j++){ first = first.getNext(); temp = temp.getNext(); } //小朋友报数,让first跟temp指针同时移动countNum-1次 while (true){ if (first == temp) break;//说明圈中就只有一个小朋友了,就结束 for (int i=0;i<countNum-1;i++){ first = first.getNext(); temp = temp.getNext(); } System.out.println("小朋友的编号是:"+first.getNo()); first = first.getNext();//让first指针后移 temp.setNext(first);//让辅助指针temp指向first。这样小朋友就出圈了 } System.out.println("最后留在圈中的小朋友编号是:"+first.getNo()); }
完整代码:
package Demo04_环形链表与约瑟夫问题; /** * 约瑟夫问题介绍: Josephu问题为:设编号为1, 2, .n的n个人围坐一圈,约定编号为k (1<=k<=n) 的人从1开始报数,数到m的那个人出列,它 的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。 n=5,即有s个人 k=1,从第一个人开始报数 m=2,数2下 * ➢提示 用一个不带头结点的循环链表来处理Josephu问题:先构成一个有n个结点的单循环链表, 然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点 的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。 思路: 构建一个单向的环形链表思路 1.先创建第一个节点让first指向该节点,并形成环形 2.后面当我们每创建一个新的节点, 就把该节点,加入到已有的环形链表中即可. 遍历环形链表 1.先让一个辅助指针(变量curBoy,指向first节点) 2.然后通过一个while循环遍历 该环形链表即可curBoy.next == first结束 */ public class Test01_约瑟夫问题 { public static void main(String[] args) { CircularLinkedList list = new CircularLinkedList(); list.addBoy(5);//加入五个小朋友 list.countBoy(1,2,5); } } /** * 创建单向环形链表类 */ class CircularLinkedList{ //创建first节点 private Boy first = null; /** * 添加小孩的节点 * nums:参与游戏的小孩个数 */ public void addBoy(int nums){ if (nums < 1){ System.out.println("您输入的数据有误!"); return; } Boy curBoy = null;//辅助指针 for (int i=1;i<=nums;i++){ Boy boy = new Boy(i);//根据编号创建小孩节点 if (i == 1){ first = boy; first.setNext(first);//构成环状 curBoy = first;//让辅助指针指向第一个节点 }else{ curBoy.setNext(boy);//从第二个小朋友开始,让辅助指针的下一个节点 指向 新的节点 boy.setNext(first);//再让第二个小朋友的下一个节点 指向 第一个小朋友 curBoy = boy;//让curBoy指向boy,也就是辅助节点向后移 } } } /** * 遍历环形链表 */ public void Traverse(){ //判断链表是否为空 if (first == null){ System.out.println("链表为空!"); return; } //因为first不能动,所以在这定义辅助指针 Boy curBoy = first; while (true){ System.out.println("小朋友的编号为:"+curBoy.getNo()); if (curBoy.getNext() == first)//说明已经遍历完毕 break; curBoy = curBoy.getNext();//让curBoy后移 } } /** * 根据用户的输入,计算出小朋友出圈的顺序 * @param startNo :表示从哪个小朋友开始报数 * @param countNum:表示数几下 * @param nums:表示最初有多少小朋友参与游戏 */ public void countBoy(int startNo,int countNum,int nums){ if (first == null || startNo < 0 || startNo > nums){ System.out.println("参数输入有误!"); return; } //创建辅助节点,帮助小朋友出圈 Boy temp = first; while (true){ if (temp.getNext() == first)//辅助指针temp事先应该指向节点的最后一个位置 break; temp = temp.getNext();//向后移动,找到最后链表的最后一个位置 } //移动小朋友到指定位置上面 for (int j = 0;j<startNo-1;j++){ first = first.getNext(); temp = temp.getNext(); } //小朋友报数,让first跟temp指针同时移动countNum-1次 while (true){ if (first == temp) break;//说明圈中就只有一个小朋友了,就结束 for (int i=0;i<countNum-1;i++){ first = first.getNext(); temp = temp.getNext(); } System.out.println("小朋友的编号是:"+first.getNo()); first = first.getNext();//让first指针后移 temp.setNext(first);//让辅助指针temp指向first。这样小朋友就出圈了 } System.out.println("最后留在圈中的小朋友编号是:"+first.getNo()); } } /** * 创建小孩类,每个小孩表示一个节点 */ class Boy{ private int no;//编号 private Boy next;//指向下一个节点(小朋友) public Boy(int no) { this.no = no; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } public Boy getNext() { return next; } public void setNext(Boy next) { this.next = next; } }