【劍指Offer】面試題45:圓圈中最後剩下的數字


一:題目描述

每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)


二:解題思路

方法一:創建一個總共有n個結點的環形鏈表,然後每次在這個鏈表中刪除第m個結點--利用數組實現


方法二:數學思想

令f(n,m)代表從n 個數中每次刪除第m 個數最後剩下的數字

在n個數中,第一個被刪除的數字(m-1)%n ,我們令 (m-1)%n=k

刪除後的序列變爲:

0,1,...,k-1,k+1,...,n-1  下次從刪除後的下一個元素k+1開始 

k+1,...,n-1,0,1,...,k-1

該序列最後剩下的數字也應該關於n和m的函數。由於這個序列的規律和前面最初的序列不一樣(最初的序列是從o開始),記做  f ' (n-1,m)

但是最後兩者剩餘的數字是相同的   即  f(n,m) = f ' (n-1,m)

(刪除一個數後的序列)--》(0--n-2的序列)

k+1      0

k+2      1

.

.

.

n-1      n-k-2

0         n-k-1

1        n-k

....

k-1     n-2

我們把這種映射關係定義p,  p(x)=(x-k-1)%n   ,x代表映射前的數,p(x)代表映射後的數

該映射的逆映射  p ' (x)=(x+k+1)%n


由於映射後的序列和最初的序列具有相同的形式,即都從0開始的連續序列,因此仍然可以用f 表示,記做 f(n-1,m)

根據我們的映射關係,映射前的序列中最後剩餘的數字  f ' (n-1,m)=p ' (f(n-1),m) =[f(n-1,m)+k+1]%n

 把(m-1)%n=k 帶入得到 f(n,m)  = f ' (n-1,m)=[f (n-1.m)+m]%n

當n=1,  序列中只有一個數字0,那麼最終剩下的數字就是0


f(n,m) =0   ,n=1

f(n,m)= [f(n-1,m)+m]%n   n>1


三:代碼實現

利用數組實現,雖然超時,想法不錯

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        if(n<=0 || m<=0)
            return -1;
        m=m%n; 
        
        //利用數組模擬環,如果刪除,值變成-1
       vector<int> num;
       for(int i=0;i<n;i++)
           num.push_back(i);
        
        int i=-1;//指向刪除後的下一個元素
        int step=0; //本輪的第幾個元素,當step==m,刪除該元素
        int count=n; //環中剩餘的元素
        
        while(count>0){
            i++;
            if(i>=n) i=0;//模擬環
            
            if(num[i]==-1) continue; //跳過已經被刪除的元素
            
            step++;
            //如果step==m,刪除i元素
            if(step==m){
                num[i]=-1;
                step=0;
                count--;
            }
        }
        //跳出循環時,i指向最後一個被刪除的元素
        return i;
    }
};

不超時,但難理解
class Solution {
public:
    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;
        
        return last;
    }
};

2017年7月30日,值得紀念的一天,終於將牛客網上劍指Offer刷完!哦耶大笑



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