问题描述
约瑟夫环就是n个人围在一起丢手绢。手绢每经过一个人报一个数,开始的时候从第一个人开始报数1,报数依次增加。若报到3的人出局,并且下一个人重新从1开始报数。直到局内剩下两个人。
问题分析
这个问题的输入有两个。第一个是n,参与游戏的人数。第二个是m,最后留下的幸存者人数。
输出有一行,是幸存者的编号。
这个问题有两种解题思路。第一种是递归法,找到递推关系。第二种则是依靠循环链表进行模拟。
实际上,这是同一个过程从前后两个方向进行推导。
解决方案
基于循环链表的解决方案便是建立一个有n个节点的循环链表,模拟其过程,得到答案。
对此,我们只需要一个指针指向第一个节点。在每次报到m时,删除当前指针指向的节点,并且下一个从1
开始报号。
public static LinkedList<Integer> josephus(int n, int m) {
LinkedList<Integer> jos = new LinkedList<Integer>();
for (int i = 1; i <= n; ++i) {
jos.add(i);
}
Iterator<Integer> it = jos.iterator();
int i = 0, a = m;
while (jos.size() >= m) {
if (!it.hasNext()) {
it = jos.iterator();
}
i++;
if (i == a) {
i = 0;
jos.removeByElement(it.next());
} else {
it.next();
}
}
return jos;
}
递归的核心便是找到每次报数时,幸存者的序号变化规律。
for (int i = m - 1; i <= n; ++i){
res = (res + m) % i;
}