Iterator eraser問題

轉(zhuǎn)載

從一個簡單的問題開始,刪除數(shù)組中某個元素后連續(xù)重復的元素,例如 1,1,2,3,3,1,1,1,4,0 ---> 1, 2,3,1,4,0。

考慮了幾秒,然后就開始動手寫代碼了:

#include <iostream>

#include <vector>

using namespace std;

int main(int argc, char* argv[])

{

? int a[] = {1, 1, 3, 3, 3, 2, 4, 1, 1, 1, 0};

? int size = sizeof(a)/sizeof(a[0]);

? vector<int> vec(a, a+size);

? vector<int>::iterator iter, end;

? int previous = vec[0];

? for (iter = vec.begin() + 1, end = vec.end(); iter != end; ++iter)

? {? ? ? ?

? ? ? if(*iter == previous)

? ? ? {

? ? ? ? vec.erase(iter);

? ? ? }

? ? ? else

? ? ? {

? ? ? ? previous = *iter;

? ? ? }? ?

? }

? for(iter = vec.begin(); iter != vec.end(); ++iter)

? {

? ? ? cout << *iter << endl;

? }

?

? return 0;

}

可是編譯一下,出來一大堆error,仔細看一下出錯信息,哦,原來自己忘記了,erase容器中元素的時候,迭代器會失效。。。頓時一身冷汗,自己平時迭代容器的時候,一般都保存了容器的end元素,要是此時迭代器失效。。。

Container<int>::iterator iter, end;

for (iter = container.begin() + 1, end = container.end(); iter != end; ++iter)

于是找到收藏的Effective STL,翻開條款9,找到了erase容器中元素的原則。以前曾經(jīng)看過,不過年深日久,早就忘得一干二凈了。現(xiàn)在還是把要點總結(jié)一下,記在blog上,供以后參考。

1. 對于關(guān)聯(lián)容器(如map, set, multimap,multiset),刪除當前的iterator,僅僅會使當前的iterator失效,只要在erase時,遞增當前iterator即可。這是因為map之類的容器,使用了紅黑樹來實現(xiàn),插入、刪除一個結(jié)點不會對其他結(jié)點造成影響。

for (iter = cont.begin(); it != cont.end();)

{

? (*iter)->doSomething();

? if (shouldDelete(*iter))

? ? ? cont.erase(iter++);

? else

? ? ? ++iter;

}

因為iter傳給erase方法的是一個副本,iter++會指向下一個元素。

2. 對于序列式容器(如vector,deque),刪除當前的iterator會使后面所有元素的iterator都失效。這是因為vetor,deque使用了連續(xù)分配的內(nèi)存,刪除一個元素導致后面所有的元素會向前移動一個位置。還好erase方法可以返回下一個有效的iterator。

for (iter = cont.begin(); iter != cont.end();)

{

? (*it)->doSomething();

? if (shouldDelete(*iter))

? ? ? iter = cont.erase(iter);

? else

? ? ? ++iter;

}

3. 對于list來說,它使用了不連續(xù)分配的內(nèi)存,并且它的erase方法也會返回下一個有效的iterator,因此上面兩種方法都可以使用。

最后給出開始那個問題的一個正確的實現(xiàn):

#include <iostream>

#include <vector>

using namespace std;

int main(int argc, char* argv[])

{

? int a[] = {1, 1, 3, 3, 3, 2, 4, 1, 1, 1, 0};

? int size = sizeof(a)/sizeof(a[0]);

? vector<int> vec(a, a+size);

? vector<int>::iterator iter = vec.begin();

? int previous = *iter;

? ++iter;

? for (; iter != vec.end();)

? {? ? ? ?

? ? ? if(*iter == previous)

? ? ? {

? ? ? ? iter = vec.erase(iter);

? ? ? }

? ? ? else

? ? ? {

? ? ? ? previous = *iter;

? ? ? ? ++iter;

? ? ? }? ?

? }

? for(iter = vec.begin(); iter != vec.end(); ++iter)

? {

? ? ? cout << *iter << endl;

? }

?

? return 0;

}

PS. 不過實際上這個問題,用vector來實現(xiàn)不是很適合,因為每次刪除一個元素,都會引起vector的一個resize操作。resize的時間復雜度是O(n),整個的resize操作要花費O(n^2)。最好是選擇list最為容器,list最適合那些需要在容器中間做插入、刪除的例子。

PS2. 另外一個可能的方法是remove_if + erase慣用法。remove_if的時間復雜度是O(n). erase的時間復雜度也是O(n),所以整個操作可以在O(n)時間里完成。(當然,對于這個例子,ShouldDeletePred比較難寫)

iterator new_end = remove_if(array.begin(), array.end(), ShouldDeletePred());

array.erase(new_end, array.end());

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn)自己的想法就是一個笑話,按理來說,應該兩天,一天去寫的,結(jié)果呢?!! 意志力,堅持對我就像笑話,自己又浪費多少...
    一覺天亮閱讀 202評論 0 0
  • 家里就我和媽媽,每天中午都會和她聊些家長里短的事情。 我們談到以后生不生孩子的問題。我說我可能以后不打算生孩子。我...
    感覺自己萌萌噠吖閱讀 151評論 0 3
  • 四大組件 Activity 典型的生命周期 onStart:Activity已經(jīng)可見了,但是還沒有出現(xiàn)在前臺,還無...
    TwistedFateJie閱讀 647評論 0 0
  • 正月十五鬧元宵,是中國的傳統(tǒng)民俗。自我懂事起的感覺,家鄉(xiāng)德慶在元宵節(jié)的“鬧”,也是遠近聞名的。可能因為德慶...
    紅肥綠瘦_b6ae閱讀 495評論 0 0