题解一:暴力法
解题思路:用数组模拟循环列表,从0开始喊到m个数后,就将其值置为-1。直到数组剩下最后一个数(非-1)。
样例如下:
图片说明

复杂度分析:
时间复杂度:O(N^2)
空间复杂度:O(N),申请一个数组标记每个小孩子是否退圈

class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        if (m == 0)return -1;
        vector<int> child(n + 1,0);//
        int k = 0,j=0;//k=出圈的小孩数,j=报数编号
        for (int i = 0;k!=n-1; i=(++i) % n) {//i=(++i) % n这里是一个特殊操作,使遍历自动再数组内循环。
            if (child[i] == 0) {
                j++;
                if (j == m) { j = 0; child[i] = 1; k++; }//此位置小孩退圈
            }
        }
        for (int i = 0; i < n; i++) {
            if (!child[i])return i;//找到最后的那个hai'zi
        }
        return -1;
    }
};

题解二: 约瑟夫问题递推公式
解题思路: 利用约瑟夫问题的递推公式 f(n,m) = ( f(n-1,m) +m)%n )
公式递推:
令f(n,m)表示最后一个人的下标。
1.假设有n个人,报数m, 从0 开始报数,易知出圈的人下标为 m-1。
2.m-1 出圈后,我们对剩余人重新编号 即 第m个人下标为0,第m+1 下标为1 ......以此编号。 那么重新编号之后,那么最后一个人的下标为f(n-1,m)
问题1: 在重新编号之后的f(n-1,m) 与 重新编号之前的f(n,m)有什么关系?
重新编号之前 m, m+1,m+2,....
重新编号之后 0 ,1 ,2,....
可知 (新编号+m)%n = 旧编号
3. f(n,m) = (f(n-1,m)+m) %n;
递归写法复杂度分析:
时间复杂度: O(N)
空间复杂度: O(N)

class Solution {
public:
    //约瑟夫
    int LastRemaining_Solution(int n, int m) {
        if(n <= 0) return -1;
        return (LastRemaining_Solution(n-1,m)+m)%n;
    }
};

改写迭代法:

class Solution {
public:
    //约瑟夫
    int LastRemaining_Solution(int n, int m) {
        if(n<=0) return -1;
        int f = 0;
        for(int i  = 2;i!=n+1;i++)
        {
            f = (m+f) % i;
        }
        return f;
    }
};