TAG
雙指針
思維方式
容器的end()
調用耗時顯著性
方法
算是很容易想到的題,然而寫起來卻很糾結…還得多練啊..
我的思路是,記錄重複的次數,初始爲0,如果相等一次,則首先+1,然後判斷是否小於2,如果是,則繼續移動雙指針並賦值;如果否,則只需移動遍歷指針即可。如果不等,則清零重複次數,移動雙指針並賦值。
糾結的問題有:
遍歷指針從哪裏開始?
uniq指針不得不從第一個開始,那麼遍歷指針如果從第一個開始,那麼上述過程就不對了。
我們需要明確的就是,uniq指針在上述邏輯表示指向uniq結果的最後一個位置。所以,根據此定義,其實遍歷指針必須從第2個位置開始(表示第一個位置以及是uniq的)!自己的糾結是邏輯不清導致的。
輸入限制
上述邏輯不能處理輸入爲空的情況!
這是因爲此邏輯成立的條件就是數組非空!因爲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()
調用其實也是一定程度耗時的(在此種情況下有些顯著啊!!)
以後如果有超大量的循環,且容器是隻讀,那麼存儲尾後迭代器或許是有必要的。