题干:

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

链接:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof

题解:

一开始打算使用暴力方式,顺着题意直接解

class Solution {
public:
    int lastRemaining(int n, int m) {
        vector<int> list(n);
        for (int i = 0; i < n; i++) {
            list[i] = i;
        }
        int idx = 0;
        while(n > 1) {
            idx = (idx + m - 1) %n;
            list.erase(list.begin() + idx);
            n--;
        }
        return list[0];
    }
};

但是无奈,无论使用vector还是linkedList复杂度都过高。vector复杂度的产生主要是删除元素需要移动许多元素,而linkedList复杂度的产生则是因为每次找到需要删除的元素得遍历,即使它删除的复杂度为O(1)。

使用暴力的方式最好的复杂度为O(mn),无法通过。

所以只能逆向思维,寻找规律。 得知最后一个值的index在最后一次一定是0,那么倒数第二次它想要安全的不被删除的话只能是 0 + m,但是0 + m可能已经超过了所有值的长度(倒数第二次是2),因为是一个环,所以直接对取余就好 所以倒数第二次的index为(0+m)%2。以此类推:

因而得到解发:

class Solution {
public:
    int lastRemaining(int n, int m) {
        int index = 0; // 最后一次为0
        for (int i = 2; i <= n; i++) {
            index = (index + m) % i;
        }
        return index;
    }
};