偶然看到一个题目:
N个人围成一圈(0~n-1),从第一个人开始报数,报到m(m-1)的人出圈,剩下的人继续从出圈的下一个人开始报数,报到m(m-1)的人出圈;如此往复,直到所有人出圈。(模拟此过程,输出出圈的人的序号)
第一眼看过去,大概感觉会是找规律的题目,
直观方法
首先想到的是,每次在出圈的人后面到末尾补上前面的人,比如N = 5, m = 3
1,2,3,4,5
第一次3出圈,然后在5后面补上前面的数字,变成1,2 ,3 ,4,5,1,2,
再从4开始,依次这样,但是发现实现起来有点困难,效率有点低。
再想了下,为什么要去移动前面的到后面,而不是直接跳到前面,然后又想了想怎么跳到前面的下标去。
这时候突然想到hashMap中利用hash获得table[]数组下标的方法,对,就是(n-1)&hash,&需要n-1是2的幂,那不需要n-1是2的幂来得到下标的是什么?(不得不说源码的各种奇淫技巧还是很有用的)
答案就是%,取模运算。
然后赶紧试了下
利用模运算
回到上面的问题,3出圈后,变成了1,2,4,5,从4开始,根据模运算,下次出圈的人下标应该为
(4的下标,暂时设为index = 2)
(index + m-1) % length ,算了下,果然不出意外的等于0,也就是编号为1的人,这样可以得到一个很清晰的思路了
- 起始位置index = 0,每次出圈的编号为(index + m-1) % length
- 更新index = (index + m-1) % length,并出圈当前index的人,长度减1更新length
- 当剩下人数不为1时,重复1,2
所以有:
ArrayList<Integer> list = new ArrayList<>();
for (int i=0; i<n; i++){
list.add(i);
}
int index = 0;
while (list.size() != 1){
index = (index + m-1) % list.size();
list.remove(index);
System.out.println(list);
}
后面觉得这样的题目肯定里面含有某种规律的,因为实在是太明显了,不过一时没推出来,然后百度了下,发现这是
约瑟夫环问题
借用https://blog.csdn.net/u011500062/article/details/72855826的图