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
模拟法,超时
将[0,n]依次存储在双端队列中
只要队列的长度不为1,就一直循环:如果到了第m个就remove;否则将其添加到链表尾部
时间复杂度为O(nm)
class Solution {
public:
int lastRemaining(int n, int m) {
deque<int> dq;
for(int i = 0;i<n;++i)
dq.push_back(i);
while(dq.size() > 1)
{
int j = 0;
while(j < m-1)
{
int tmp = dq.front();
dq.pop_front();
dq.push_back(tmp);
++j;
}
dq.pop_front();
}
return dq.front();
}
};
假设当前删除的位置是 idxidx ,下一个删除的数字的位置是 idx + midx+m 。但是,由于把当前位置的数字删除了,后面的数字会前移一位,所以实际的下一个位置是 idx + m - 1idx+m−1。由于数到末尾会从头继续数,所以最后取模一下,就是 (idx + m - 1) \pmod n(idx+m−1)(modn)。
class Solution {
public:
int lastRemaining(int n, int m) {
deque<int> dq;
for(int i = 0;i<n;++i)
dq.push_back(i);
int idx = 0;
while(n > 1)
{
idx = (idx + m - 1) % n;
dq.erase(dq.begin() + idx);
--n;
}
return dq.front();
}
};
公式法
class Solution {
public:
int lastRemaining(int n, int m) {
int ans = 0;
// 最后一轮剩下2个人,所以从2开始反推
for (int i = 2; i <= n; i++) {
ans = (ans + m) % i;
}
return ans;
}
};