经典的约舍夫问题,其实归根到底是单向环形链表的问题,本篇博客先介绍单向循环链表,再以一个小游戏丢手绢小孩出圈的问题理解约瑟夫问题
单向循环链表
1、示意图
2、构建环形链表
(1)先创建第一个节点,让first指向该节点,并形成环形
(2)当我们每创建一个新的节点,就把该节点加入到已有的环形链表中
3、遍历环形链表
(1)想让一个辅助变量curBoy,指向first节点
(2)然后通过一个while循环遍历该环形链表即可,curBoy.next=first结束
4、代码实现
class CircleSingleLinkedList{
//创建first节点,当前没有编号
private Child first=new Child(-1);
//添加节点,构建环形链表
public void addChild(int nums){
if(nums<1){
System.out.println("num的值不正确");
return;
}
Child curBoy=null;
for (int i = 1; i <= nums; i++) {
Child boy=new Child(i);
if (i==1){
first=boy;
first.setNext(first);
curBoy=first;
}else {
curBoy.setNext(boy);
boy.setNext(first);
curBoy=boy;
}
}
}
//循环当前环形链表
public void showChild(){
if(first==null){
System.out.println("没有小孩");
return;
}
Child curBoy=first;
while (true){
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if(curBoy.getNext()==first){
break;
}
curBoy=curBoy.getNext();
}
}
}
class Child{
private int no;
public Child next;
public Child(int no){
this.no=no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Child getNext() {
return next;
}
public void setNext(Child next) {
this.next = next;
}
}
5、测试构建的单向环形链表
public class Test{
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList();
circleSingleLinkedList.addChild(5);
circleSingleLinkedList.showChild();
}
}
运行结果
约瑟夫问题
设编号为1,2,3,……n的n个人围一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
1、具体例题
根据用户的输入,生成一个小孩出圈的顺序
n=5,有5个人
k=1,从第一个人开始报数
m=2,数2下
其出圈顺序是:2->4->1->5->3,下边运行结果测试正好验证
2、分析过程
(1)需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
(2)小孩报数前,先让first和helper移动k-1次
(3)当小孩报数时,让first和helper指针同时移动m-1次
(4)这时就可以将first指向的小孩节点出圈
first=first.next
helper.next=first
原来first指向的节点就没有任何引用,就会被回收
3、代码实现
在单向循环链表CircleSingleLinkedList中添加countChild方法
/**
* 根据用户的输入,计算出小孩出圈的顺序
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有几个小孩在圈中
*/
public void countChild(int startNo,int countNum,int nums){
//校验数据
if(first==null||startNo<1||startNo>nums){
System.out.println("参数输入有误,请重新输入");
return;
}
//辅助指针
Child 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次,然后出圈
while(true){
if (helper==first){
break;
}
//让first和helper同时移动countNum-1
for (int j = 0; j < countNum-1; j++) {
first=first.getNext();
helper=helper.getNext();
}
//first指向的节点就是出圈的小孩节点
System.out.printf("小孩%d出圈\n",first.getNo());
first=first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈中的小孩编号%d\n",helper.getNo());
}
4、测试小孩出圈的顺序
public class Test{
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList();
circleSingleLinkedList.addChild(5);
//测试小孩出圈
circleSingleLinkedList.countChild(1,2,5);
}
}
运行结果
扫描二维码关注公众号,回复:
11162497 查看本文章