【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;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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