C 順序容器和關聯容器的刪除

2021-08-22 11:54:19 字數 3495 閱讀 6246

乙個合格的容器(container)必須支援增刪改查,c++的順序容器和關聯容器也不例外。

不同於insert和search,erase操作涉及到多種情況,尤其是和迭代器相結合的時候。

本文總結常用c++常用容器的erase的正確做法。

vector是stl中唯一乙個保證記憶體連續的容器,也是我們最常用的容器。

刪除值為val的元素

void erasevec()

; vec.erase(std::remove(vec.begin(), vec.end(), 5), vec.end());

}

上述**是最標準的在vector中刪除值為5的元素的方法,你會發現有兩個很容易讓人產生誤會的介面:

erase和remove,我們看一下他們的原型:

iterator erase (iterator

begin, iterator

end);

//tips : 刪除容器中迭代器在[first; last)範圍內的元素

//return : 最後乙個被刪除的元素的下乙個元素的迭代器

iterator

remove(iterator

begin, iterator

end, val);

//tips : 移除容器中迭代器在[first; last)範圍內的值為val的元素

//return : 被成功移除的之後,剩餘的有效元素的下乙個元素的迭代器

我們可以看到,remove做的操作是移除而erase是刪除,這兩者到底有什麼區別呢?

我們剛建立的vec的記憶體布局如下:

在remove(begin,end,5)之後,記憶體布局如下:

它僅僅做了移除操作,把不要的元素放在了末尾(這是其中一種實現,你不能對67後面的兩個位置的值做出任何假設),但是capacity甚至size都沒有改變,也就是說元素並沒有被刪除。

更可怕的是,如果你做了下列操作:

std::remove(vec.begin(), vec.end(), 5);

for (auto it = vec.begin(); it != vec.end(); ++it)

你會發現輸出的值是12346755(或者可能是132456767),元素根本沒有被刪除

vec.erase(std::find(vec.begin(), vec.end(), 5))操作之後,記憶體布局:

元素被真正的刪除了。

再次強調一下最正確的刪除方式:

void erasevec()

; vec.erase(std::remove(vec.begin(), vec.end(), 5), vec.end());

}

刪除滿足某個條件的元素
void erasevecif()

; vec.erase(std::remove_if(vec.begin(), vec.end(),

(int elem)

), vec.end());

}

迴圈刪除

我們再看一種比較容易犯錯的情況,那就是在迴圈種刪除某個迭代器:

void eraseveclooperror()

; for (std::vector

::iterator it = vec.begin(); it != vec.end(); ++it)}}

這裡是錯誤的示範,為何呢?

原因在於,vec.erase(it)會讓vec中的迭代器失效,直接it++會讓程式崩潰,所以這裡正確的做法應該是合理利用erase的返回值:

void erasevecloopcorrect()

; for (std::vector

::iterator it = vec.begin(); it != vec.end(); /*do nothing*/)

else}}

deque和vec的方法完全相同,不再贅述

string和vec的方法完全相同,不再贅述

list和vector、dequeue、string不同,它的刪除更為簡單:

void eraselist()

; ls.remove(5);

ls.remove_if((int elem) );

}

對於list,直接使用remove方法即可。(對於list而言移除 == 刪除

當然,你仍可以使用erase介面,只不過remove方法更為效率一點。

迴圈刪除的方式

void eraselistloop()

; for (std::list

::iterator it = ls.begin(); it != ls.end(); )

else}}

map和unordered_map

由於map和unordered_map的特殊性(他們的元素都是乙個pair),c++沒有提供這樣的介面

int x = 5;

container.erase(x)

container.remove(x)

這樣的介面,如果想完成類似的需求那麼只能:

void eraseset()

}

這個是比較符合我們的直覺的,但是如果在迴圈種刪除滿足某種條件的迭代器,那麼就需要好好注意了:

void erasemaperror()}}

處於同樣(見上文,vector:迴圈刪除)的原因,我們不能再迴圈種直接it++,可是對於關聯容器,erase這個方法沒有返回iterator,所以正確的做法是:

void erasemapcorrect()

else}}

set和unordered_set

set和unordered_set可以直接使用erase方法,但是remove不起作用,使用remove會編譯錯誤:

void eraseset()

迴圈刪除則同map和unordered_map,不再贅述。

容器 順序容器 關聯容器

容器分為 順序容器 關聯容器,示意圖如下 順序容器 只儲存值,關聯容器 key value形式,關聯容器的乙個元素包含兩個部分 鍵值對 key value 有序容器 底層實現為紅黑樹,即容器內的key是有序的 無序容器 底層實現為hash table,雜湊表 區別 順序容器通過元素在容器中的位置順序...

關聯容器和順序容器的差別

關聯容器和順序容器的本質差別在於關聯容器通過鍵 key 儲存和讀取元素,順序容器則通過元素在容器中的位置順序儲存和訪問元素。順序容器list vector deque和關聯容器map set型別分別適用的情況 1 list 型別適用於需要在容器中間插入和刪除元素的情況,例如以無序方式讀入一系列學生資...

順序容器與關聯容器的不同

想要理解關聯容器與順序容器的不同,最關鍵的是理解其基礎的資料結構,這樣就很自然的理解它所表現出來的性質。兩類容器的根本差別在於,順序容器中的元素是 順序 儲存的 鍊錶容器中的元素雖然不是在記憶體中 連續 儲存的,但仍然是按 順序 儲存的 理解順序的關鍵,是理解容器支援的操作形式以及效率。對vecto...