单向环形链表
Josephu(约瑟夫、约瑟夫环)问题
Josephu问题为:设编号为1,2,... n个人围坐一圈,约定编号为k(1<=k<=n)的人开始报数,数到m的那个人出列,他的下一位又从1开始报数,报到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示:用一个不带头节点的循环链表来处理Josephu问题,先 构成一个有n个节点的单循环链表,然后由k节点从1开始计数,计数到m时,对应节点从链表中删除,然后再从被删除节点的下一个节点又从1开始计数,直到最后一个节点从链表中删除算法结束。
图示:
应用举例:
代码
package com.atxihua; public class Josepfu { public static void main(String[] args) { //测试 CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList(); circleSingleLinkedList.addBoy(5);//加入5个小孩节点 circleSingleLinkedList.showBoy(); //测试出圈是否正确 circleSingleLinkedList.countBoy(1,2,5); } } //创建一个Boy类,表示一个节点 class Boy{ private int no;//编号 private Boy next;//指向下一个节点,默认null 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; } } //创建一个环形的单向链表 class CircleSingleLinkedList{ //创建一个first节点,当前没有编号 private Boy first=null; //添加小孩节点,构建成一个环形链表 public void addBoy(int nums){ //做一个nums的数据校验 if(nums<1){ System.out.println("nums的值不正确"); return; } Boy curBoy=null;//辅助指针,帮助构建环形链表 //使用for来创建我们的环形链表 for(int i=1;i<=nums;i++){ //根据编号,创建小孩节点 Boy boy=new Boy(i); //如果是第一个小孩 if(i==1){ first=boy; first.setNext(first);//构成环 curBoy=first;//让curBoy指向第一个小孩 }else{ curBoy.setNext(boy);//如果不是第一个小孩,那么将curBoy的下一节点设置为boy boy.setNext(first);//将新添加的节点的next,指向first,因为first不会动,将新增数据的节点指向first,始终构成环形链表 //这里与单向链表的区别,只是普通的单向链表最后一个节点的next节点为null,这里指向第一个节点first,构成环形 curBoy=boy;//将curBoy指向新增的小孩,方便下次添加小孩(节点) } } } //遍历当前的环形链表 public void showBoy(){ //判断链表是否为空 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后移 } } /* * 根据用户的输入,计算出小孩出圈的顺序 * startNo表示从第几个小孩开始数数 * countNum表示数据下 * nums表示最初有多少小孩在圈中 * */ public void countBoy(int startNo,int countNum,int nums){ //先对数据进行校验 if(first==null||startNo<1||startNo>nums){ System.out.println("参数输入有误,请重新输入"); return; } //创建要给辅助指针,帮助完成小孩出圈 Boy helper=first; //需要创建一个辅助指针helper,事先应该指向环形链表的最后这个节点 while (true){ if(helper.getNext()==first){//说明helper指向最后小孩节点 break; } helper=helper.getNext(); } //小孩报数前,先让first和helper移动k-1次 for (int j=0;j<startNo-1;j++){ first=first.getNext(); helper=helper.getNext(); } //当小孩报数前,先让firstt和helper指针同时移动m-1次,然后出圈 //这里是一个循环操作,直到圈中只有一个节点 while (true){ if(helper==first){//说明圈中只有一个节点 break; } //让first和helper指针同时移动coutNum-1 for (int j=0;j<countNum-1;j++){ first=first.getNext(); helper=helper.getNext(); } //这时first指向的节点,就是要出圈的小孩节点 System.out.println("编号为"+first.getNo()+"的小孩出圈"); //将first指向的节点的小孩出圈 first=first.getNext(); helper.setNext(first); } System.out.println("编号为"+first.getNo()+"的小孩最后出圈"); } }
运行结果:
出圈顺序为:2->4->1->5->3