剑指 Offer 62. 圆圈中最后剩下的数字

题目描述:

0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例:

输入: n = 5, m = 3
输出: 3

输入: n = 10, m = 17
输出: 2

思路:

思路①:创立数组,每当到m-1位置时,删除数组中元素,然后将位置计0重新开始

思路②:

  1. n个人编号0,1,2,…,n-1,每数m次删掉一个人
  2. 假设有函数f(n)表示n个人最终剩下人的编号
  3. n个人删掉1个人后可以看做n-1的状态,不过有自己的编号。
  4. n个人删掉的第一个人的编号是(m-1)%n,那么n个人时删掉第一个人的后面那个人(m-1+1)%n一定是n-1个人时候编号为0的那个人,即n个人时的编号m%n(这个编号是对于n个人来考虑的),n-1个人时编号为i的人就是n个人时(m+i)%n
  5. 所以f(n)=(m+f(n-1))%n
  6. f(1)=0,因为1个人时只有一个编号0。因此可以将人数从2反推到n

思路②(另一种解释):
在这 n个数字中, 第一个被删除的数字是(m-1)%n。为了简单起见,我们把(m- 1)%n 记为 k,那么删除k之后剩下的 n-1 个数字为 0,1,… ,k-1,k+1,… ,n-1,并且下一次删除从数字 k+1 开始计数。相当于在剩下的序列中, k+1 排在最前面,从而形成 k+1,… ,n- 1,0,I,… ,k-1 。该序列最后剩下的数字也应该是关于 n 和 m 的函数。由于这个序列的规律和前面最初的序列不一样(最初的序列是从 0 开始的连续序列),因此该函数不同于前面的函数,记为 f’(n-1,m)。最初序列最后剩下的数字 f(n, m)一定是删除一个数字之后的序列最后剩下的数字,即 f(n, m)=f’(n-1, m)。

接下来我们把剩下的这 n-1 个数字的序列 k-1, …,n-1,0,1,… ,k-1 做一个映射,映射的结果是形成一个从 0 到 n-2 的序列:
在这里插入图片描述

代码:

思路①:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var lastRemaining = function(n, m) {
if(n<1||m<1) return -1;
var arr=[];
for(var i=0;i<n;i++) arr.push(i);
var idx=0;
var start=0;
while(arr.length>1){
for(var i=1;i<m;i++){//找到第m个位置
idx=(idx+1)%arr.length;
}
arr.splice(idx,1)
}
return arr[0];
}

思路②:

1
2
3
4
5
6
7
var lastRemaining = function(n, m) {
let result = 0;
for(let i = 2; i <= n; i++) {
result = (result + m) % i;
}
return result
};