【LeetCode 面试题 62】圆圈中剩下的数字

(可提交代码)原题链接:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/

题目:

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)
    1. 数到0,是第1个数字,数下一个。此时的数字顺序变为:1、2、3、4、0。
    2. 数到1,是第2个数字,数下一个。此时的数字顺序变为:2、3、4、0、1。
    3. 数到2,是第3个数字,删掉。此时的数字顺序变为:3、4、0、1。
    4. 数到3,是第1个数字,数下一个。此时,删掉后的数字为第1个,数字顺序变为:4、0、1、3。
    5. 数到4,是第2个数字,数下一个。此时的数字顺序变为:0、1、3、4。
    6. 数到0,是第3个数字,删掉。此时的数字顺序变为:1、3、4。
    7. 数到1,是第1个数字。此时,删掉后的数字为第1个。此时的数字顺序变为:3、4、1。
    8. 数到3,是第2个数字,数下一个。此时的数字顺序变为:4、1、3。
    9. ...
  2. 总结规律
    1.  每数到一个数字,若不是第m个,就把他放到数字顺序的最后一项;若是第m个,就直接删掉。
  3. 选择合适的数据结构:链表或者队列
  4. 整理思路,写代码。
  5. int lastRemaining(int n, int m) {
    
            if(n==1) return 0;
            if(m==1) return n-1;
    
            queue<int> que;
            for(int i=0;i<n;i++)
            {
                que.push(i);
            }
            int count=1;
            while(que.size()>1)
            {
                if(count==m)
                {
                    que.pop();
                    count=1;
                }
                else{
                    int t=que.front();
                    que.pop();
                    que.push(t);
                    count++;
                }
            }
            return que.front();
        }

    存在问题:超时。

  6.  

    再次总结规律,减少代码执行时间

    参考网址:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/si-chong-fang-fa-xiang-xi-jie-da-by-yuanninesuns/

    https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/yue-se-fu-huan-wen-ti-tu-jie-xiang-xi-si-lu-fen-xi/

     第1次删除的是数字列表中第(m-1)%n个,设c=(m-1)%n,之后每次删除的是第(c+m-1)%list.size() 个。

    存在问题:依旧超出时间限制。

    list与vector区别:https://www.csdn.net/gather_26/NtDakgysMjMtYmxvZwO0O0OO0O0O.html

    vector删除元素用法:https://blog.csdn.net/qq_36770641/article/details/89355657

    int lastRemaining(int n, int m) {
    
            if(n==1) return 0;
            if(m==1) return n-1;
    
            vector<int> vec;
            for(int i=0;i<n;i++)
            {
                vec.push_back(i);
            }
            
            int c=(m-1)%n;        
            while(vec.size()>1)
            {
                //删除list中第c个元素            
                vec.erase(vec.begin()+c); 
                //更新c值
                c=(c+m-1)%(vec.size());            
            }        
            return vec[0];
        }

     

     

解题思路二:使用数学解法

      n 个数字的编号如下:0、1、2、3、...、k-1、k、k+1、...、n-1
      第一个被剔除的数字是k=(m-1)% n,剔除完结果如下:0、1、2、3、...、k-1、k、k+1、...、n-1

      那下一个位置那就是从 k+1 开始啦,我们给它重新排个队,再编个号:

      k+1、k+2、k+3、...、n-1、0、1、2、3、...、k-1

      0       1         2                                                   n-2

    把映射数字记为x,原始数字记为y,那么映射数字变回原始数字的公式为  y=(x+k+1) mod n

    在映射数字中,n-1个数字,不断删除第m个数字,由定义可以知道,最后剩下的数字为f(n-1,m)。我们把它变回原始数字,由上一个公式可以得到最后剩下的原始数字是(f(n-1,m)+k+1)%n,而这个数字也就是一开始我们标记的f(n,m),所以可以推得递归公式为  f(n,m) =(f(n-1,m)+k+1)mod n

     将k=(m-1)%n代入,化简得到:f(n,m) =(f(n-1,m)+m)\mod n, 且f(1,m) = 0

     代码中可以采用迭代或者递归的方法实现该递归公式。时间复杂度为O(n),空间复杂度为O(1)
     注意公式中的mod就等同于%,为取模运算。值得注意的是,在数学中,下式成立:(a%n+b)%n=(a+b)%n

代码:

//迭代 
int lastRemaining(int n, int m) {

        if(n==1) return 0;
        if(m==1) return n-1;
        //自底向上
        int flag=0;
        for(int i=2;i<=n;i++)
        {
            //i表示有i个数的时候
            flag=(flag+m)%i;
        }
        return flag;
    }

//递归
 int lastRemaining(int n, int m) {
        if(n==1) return 0;
        if(m==1) return n-1;
        
        return (lastRemaining(n-1,m)+m)%n;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章