约瑟夫问题
思路
构建一个单向的环形链表
- 先创建第一个节点,让first指向该节点,并形成环形
- 后面当我们每创建一个新的节点,就把该节点加入到已有的环形链表
遍历环形链表
- 先让一个辅助指针(变量)curboy指向first节点
- 然后通过一个while循环遍历该环形链表即可curBoy.next==first结束
出圈顺序
-
创建一个辅助指针helper,事先应该指向环形链表的最后这个节点
(在报数前,先让first和helper移动k-1次,即移动到k个小孩这)
-
当小孩报数时,让first和helper指针同时移动m-1次
-
这时就可以将first指向的小孩子出圈
first=first.next
helper.next=first
原来first指向的节点就没有任何引用,就会被回收
方法1
public class Josepfu {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.showBoy();
circleSingleLinkedList.countBoy(2,2,5);
}
}
//创建一个环形的单向链表
class CircleSingleLinkedList{
//创建一个first节点,当前没有编号
private Boy first=null;
public void addBoy(int nums){
//数据校验
if(nums<1){
System.out.println("num的值不正确");
return;
}
//辅助指针,帮助构建环形链表
Boy curBoy=null;
//使用for来创建环形链表
for (int i = 1; i <=nums; i++) {
Boy boy = new Boy(i);
if(i==1){
first=boy;
boy.setNext(first);//构成环
curBoy=first;//让curBoy指向第一个小孩
}else {
curBoy.setNext(boy);
boy.setNext(first);
curBoy=boy;
}
}
}
//遍历当前环形链表
public void showBoy(){
if (first==null){
System.out.println("没有任何小孩");
return;
}
Boy curBoy=first;//因为first不能动,所以需要一个辅助指针完成遍历
while (true){
System.out.printf("小孩的编号 %d \n",curBoy.getNo());
if (curBoy.getNext()==first){
break;
}
curBoy=curBoy.getNext();
}
}
/**
* 根据用户输入,计算小孩出圈的顺序
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有多少小孩在圈中
*/
public void countBoy(int startNo,int countNum,int nums){
//校验
if(first==null || startNo<1 ||startNo>nums){
System.out.println("参数输入有误,请重新输入");
return;
}
Boy helper=first; //帮助完成小孩出圈
while (true){
if(helper.getNext()==first) break;
helper=helper.getNext();
}
//在报数前,先让first和helper移动k-1次,即移动到k个小孩这
for (int j = 0; j < startNo-1 ; j++) {
first=first.getNext();
helper=helper.getNext();
}
//循环操作,直到圈中只有一个节点
int i=1;
while (true){
if(helper==first){
break;
}
for (int j = 0; j < startNo-1 ; j++) {
first=first.getNext();
helper=helper.getNext();
}
//这时first指向的节点,就是要出圈的小孩节点
System.out.printf("小孩%d出圈\n",first.getNo());
//将first指向的小孩出圈
first=first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈中的小孩编号时%d \n",first.getNo());
}
}
//创建一个boy类,表示一个节点
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;}
}
方法2
public void countBoy(int startNo,int countNum,int nums){
//校验
if(first==null || startNo<1 ||startNo>nums){
System.out.println("参数输入有误,请重新输入");
return;
}
Boy helper=first; //帮助完成小孩出圈
while (true){
if(helper.getNext()==first) break;
helper=helper.getNext();
}
//在报数前,先让first和helper移动k-1次,即移动到k个小孩这
for (int j = 0; j < startNo-1 ; j++) {
first=first.getNext();
helper=helper.getNext();
}
//循环操作,直到圈中只有一个节点
int i=1;
while (true){
if(helper==first){
System.out.printf("最后留在圈中的小孩编号时%d \n",first.getNo());
break;
}
if(i==countNum){
System.out.printf("小孩%d出圈\n",first.getNo());
first=first.getNext();
helper.setNext(first);
i=1;
}
first=first.getNext();
helper=helper.getNext();
i++;
}
}
}