[LeetCode]problem 80. Remove Duplicates from Sorted Array II

link

TAG

雙指針

思維方式

容器的end()調用耗時顯著性

方法

算是很容易想到的題,然而寫起來卻很糾結…還得多練啊..

我的思路是,記錄重複的次數,初始爲0,如果相等一次,則首先+1,然後判斷是否小於2,如果是,則繼續移動雙指針並賦值;如果否,則只需移動遍歷指針即可。如果不等,則清零重複次數,移動雙指針並賦值。

糾結的問題有:

  1. 遍歷指針從哪裏開始?

    uniq指針不得不從第一個開始,那麼遍歷指針如果從第一個開始,那麼上述過程就不對了。

    我們需要明確的就是,uniq指針在上述邏輯表示指向uniq結果的最後一個位置。所以,根據此定義,其實遍歷指針必須從第2個位置開始(表示第一個位置以及是uniq的)!自己的糾結是邏輯不清導致的。

  2. 輸入限制

    上述邏輯不能處理輸入爲空的情況!

    這是因爲此邏輯成立的條件就是數組非空!因爲uniq表示uniq部分的最後一個位置,如果數組未空,那麼根本不存在uniq部分。

以上的方法很多條件判斷。

完成上述代碼後,看了DISCUSS,第一個HOT的答案真是讓人難以置信!原來可以如此簡潔與簡單!

我把代碼貼過來吧:

int removeDuplicates(vector<int>& nums) {
    int i = 0;
    for (int n : nums)
        if (i < 2 || n > nums[i-2])
            nums[i++] = n;
    return i;
}

我將其改寫爲等價的如下代碼:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        size_t sz = nums.size() ;
        if(sz < 3) { return sz ;}
        size_t rear_pos = 1 ; 
        for(size_t pos = 2; pos < sz ; ++pos)
        {
            if(nums[pos] != nums[rear_pos-1])
            {
                nums[++rear_pos] = nums[pos];
            }
        }
        return rear_pos + 1 ;
    }
};

核心就是——不需要要記錄有多少重複的數字,只需判斷已有結果的狀態!如果當前遍歷指針的值與當前uniq指針(uniq部分的尾位置)的前一個位置的值相等(代碼中是大於關係,其實可以改爲不等於),那麼就必然有:結果中已經有兩個數相同了。

這其實是兩種思維方式

我的想法,很常規,就是考慮複製指針的狀態,看目前已經有多少個重複的了。

題解的想法,是考慮已有結果的狀態!看結果中是否已經有兩個相等了(因爲有序,只需一個位置判斷)。

代碼

這裏貼上自己想的方法,相對繁瑣。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size() < 3) { return nums.size() ; }
        const int DUPLICATE_LIMIT = 2 ;
        vector<int>::iterator dup_uniq_iter = nums.begin() ;
        int duplicates_cnt = 0 ;
        for(auto iter = nums.begin() + 1; iter < nums.end() ; ++iter)
        {
            if(*iter == *dup_uniq_iter)
            {
                ++duplicates_cnt ;
                if(duplicates_cnt < DUPLICATE_LIMIT)
                {
                    *++dup_uniq_iter = *iter ;
                }
            }
            else
            {
                *++dup_uniq_iter = *iter ;
                duplicates_cnt = 0 ;
            }
        }
        size_t newSz = dup_uniq_iter - nums.begin() + 1 ;
        nums.resize(newSz);
        return newSz;
    }
};

後記

關於迭代次與下標的速度比較:

對第二種方法,我寫的迭代器版本:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        size_t sz = nums.size() ;
        if(sz < 3){ return sz ; } // always satisfied
        vector<int>::iterator rear_iter = nums.begin() + 1 ;
        for(auto iter = nums.begin()+2 ; iter != nums.end(); ++iter)
        {
            if(*iter != *(rear_iter-1))
            {
                *++rear_iter = *iter ;
            }
        }
        return rear_iter - nums.begin() + 1 ;
    }
};

以上與下標版邏輯完全一致,然而迭代器版本跑了20ms,而下標版只需要16ms

這之間的差距時如何形成的呢?

注意for循環中的nums.end()調用!當我把它在循環前保存下來作爲一個變量來代替時,即:

//for(auto iter = nums.begin()+2 ; iter != nums.end(); ++iter)
auto end_iter = nums.end() ;
for(auto iter = nums.begin()+2 ; iter != end_iter; ++iter)

迭代器的版本時間消耗也成爲了16ms!

由此可見容器的end()調用其實也是一定程度耗時的(在此種情況下有些顯著啊!!)

以後如果有超大量的循環,且容器是隻讀,那麼存儲尾後迭代器或許是有必要的。

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