描述
        每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。如果没有小朋友,请返回-1。

示例1
输入:5,3
返回值:3

方法一:递归求解
核心思想:
        本题是一个比较经典的约瑟夫环问题,多个人围成一圈,然后给定一个数字和起始位置,从起始位置开始数,数到给定数字,则该位置的人出局,游戏继续从出局的下一个人开始,直到最后一个人为止游戏结束。
假设f(N,M)表示N个人报数,每次报数到M时,则当前报数人出局;
则f(N-1,M)表示N-1个人报数,每次报数到M时,则当前报数人出局。
f(N,M)=(f(N-1,M)+k)%N=(f(N-1,M) +M%N)%N=(f(N-1,M)+M)%N (N>1,k=M%N)

可以得出:f(N,M) = (f(N-1,M)+M)%N

核心代码:

int f(int n,int m){
    if(n == 1)
        return 0;
    else
        return (f(n-1,m) + m) % n;
}

int LastRemaining_Solution(int n, int m) {
    if(n<1|| m<1)
        return -1;
    return f(n,m);
}

复杂度分析:
        递归n次,因此时间复杂度为O(N)。而每次递归都需要一个额外的变量保存,因此空间复杂度为O(N)
时间复杂度:O(N)
空间复杂度:O(N)

方法二:约瑟夫环递推公式
核心思想:
        本题是一个比较经典的约瑟夫环问题,多个人围成一圈,然后给定一个数字和起始位置,从起始位置开始数,数到给定数字,则该位置的人出局,游戏继续从出局的下一个人开始,直到最后一个人为止游戏结束。
假设f(n)表示0~n-1共n个数字中最后剩下的数字,去掉的数字为(m-1)%n,而下一次报数是从m%n个数字开始(即出局的下一个为止),因此
f(n-1) 和 f(n)之前的关系如下:
f(n-1) f(n)
0 m%n
1 (m+1)%n
... ...
n-2 (m-2)%n

因此可以归纳总结:f(n) = (f(n-1)+m)%n (其中n是变化的,因为每次操作都会有人出局)

图解:
图片说明

核心代码:

int LastRemaining_Solution(int n, int m)
{
    if(n<1|| m<1)
        return -1;
    int last=0;
    for(int i=2;i<=n;i++)   
        last=(last+m)%i;    //这里的last相当于上面递推式中的f(n),i相当于n
    return last;
}

复杂度分析:
        代码使用了循环,循环次数为数组大小,因此该方法的时间复杂度为O(N)。由于没有采用了额外的数组空间,空间复杂度为O(1)
时间复杂度:O(N)
空间复杂度:O(1)