0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
示例 2:
输入: n = 10, m = 17
输出: 2
限制:
1 <= n <= 10^5
1 <= m <= 10^6
基本思路:用双端队列模拟环,如果是第m个元素,出队,否则是先出队,再从队尾入队
int lastRemaining(int n, int m) {
deque<int> dq;
int cnt=0;
for(int i=0;i<n;i++){
cnt++;
cnt=cnt%m;
if(cnt!=0)
dq.push_back(i);
}
while(dq.size()>1){
cnt++;
cnt=cnt%m;
int top=dq.front();
dq.pop_front();
if(cnt!=0){
dq.push_back(top);
}
}
return dq.empty()?n-1:dq.front();
}
基本思路:从最后一个剩余元素x(下标为零),逆向向上推到其在原始队列中的位置,假设f(n,m)表示的是在长度为n的队列中,x的位置
- 显然f(1,m)=0,因为最后只剩下最后一个元素
- 若已知f(n-1,m)=x,怎么推出f(n,m)?先将删除的元素补到n-1的末尾,然后再将该序列右移m位,使两个队列对齐,需要模上n,就是其坐标,即f(n,m)=(f(n-1,m)+m)%n; 下图来自于COOLUCAS的换个角度举例解决约瑟夫环
int lastRemaining(int n, int m) {
int ans=0;
for(int i=2;i<=n;i++){
ans=(ans+m)%i; //最后一轮数组大小为2,逆向还原最后一个元素的位置
}
return ans;
}