今天我给大家带来用环形链表解决约瑟夫问题的方法,环形链表的实现可以查看我上一篇文章:
我们今天就直接用我已经实现的环形链表就行了,
我们先来看看什么是约瑟夫问题:
什么是约瑟夫问题?
约瑟夫问题也称约瑟夫环,它的具体描述如下:
设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第K个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。
比如说我们现在有5个小孩在圈,我们要从第1小孩个开始报数:
那么出圈的过程应该是这样:
所以最后留在圈中的小孩的编号为3,出圈序列为2,4,1,5,3。
思路分析
1、既然涉及到出圈,那必然要将链表中的节点删除,因为我们使用的是单向环形链表所以我们需要一个辅助指针helper辅助我们删除节点,我们实现应该让指针helper指向链表的最后一个节点:
2、在小孩报数前,想要让first和helper移动k-1次。
3、当小孩报数时,让first和helper指针同时移动m-1次(因为自身也要数一次)。
4、当我们找到要出圈的小孩时,就可将其出圈(删除)了:
我们只需要进行以下操作:
First = first.next;
Heper.next = first;
就可以将fist原来指向的节点出圈了:
因为此时first原来指向的节点已经没有任何引用,所以它就会被垃圾回收机制所回收了。
代码实现
我们直接在我们之前的环形链表的类里添加一个解决约瑟夫问题的方法即可:
// 解决约瑟夫问题
public void josepfu(int k, int m, int n){
// 先要对数据的合理性进行校验
if(first == null || k == 0 || k > n){
System.out.println("没有小孩在圈或参数输入有误");
return;
}
Kid helper = first; // 辅助指针,小孩出圈
// 先让helper指向链表的最后一个节点
while(true){
if(helper.next == first){
break;
}
helper = helper.next;
}
// 让first指向第k个节点
for(int i = 0; i < k - 1; i++){
first = first.next;
helper = helper.next;
}
// 出圈操作
while(first.next != first){
// 让first找到要出圈的节点
for(int j = 0; j < m - 1; j++){
first = first.next;
helper = helper.next;
}
System.out.printf("小孩%d出圈\n",first.no);
first = first.next;
helper.next = first;
}
System.out.printf("小孩%d出圈\n",first.no); // 将最后一个小孩也出圈
}
我们可以来测试一下:
运行结果:
其结果和我们上面举例的结果一样