解法1:List模拟环形链表

最开始长度为n,每次删除一个数,长度变为n-1,如果用数组模拟操作的话,删除一个数据,涉及大量的数据搬移操作,所以我们可以使用链表来模拟操作。

public int LastRemaining(int n, int m){
        LinkedList<Integer> list=new LinkedList<>();
        if(m<1 || n<1){
            return -1;
        }
        for(int i=0;i<n;i++){
            list.add(i);
        }
        int bt=0;
        while(list.size()>1){
            bt=(bt+m-1)%list.size();
            list.remove(bt);
        }
        return list.get(0);
    }

解法2:反推

设有n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。令f[i]表示i个人时最后胜利者的编号,则有递推公式:

  • f[1] = 0
  • f[2] = (f{1] + m) % 2
  • f[3] = (f[2] + m) % 3
  • ...
  • f[n] = (f[n-1] + m) % n
    public int LastRemaining2(int n, int m){
        if (m < 1 || n < 1)
            return -1;
        int last = 0;
        // i代表有目前有个人 
           //最后一轮剩下2个人,所以从2开始反推
        for (int i = 2; i <= n; i++)
            last = (last + m) % i;
        return last;
    }