簡體   English   中英

std :: remove和擦除向量之間的區別?

[英]Difference between std::remove and erase for vector?

我懷疑我想澄清一下。 我知道erasestd::remove之間std::vector的不同行為,其中第一個物理上從向量中刪除元素,從而減小大小,而另一個僅移動元素而使容量不變。

這僅僅是出於效率原因嗎? 通過使用erasestd::vector中的所有元素將移位1,從而導致大量復制; std::remove只是執行“邏輯”刪除操作,並且通過四處移動來使向量保持不變。 如果物體很重,這種差異可能很重要,對不對?

這僅僅是出於效率原因嗎? 通過使用擦除,std :: vector中的所有元素都將移位1,從而導致大量復制; std :: remove只是執行“邏輯”刪除操作,並且通過四處移動來使向量保持不變。 如果物體很重,差異會很重要,對不對?

使用這個習慣用法的原因正是這樣。 性能上有好處,但單次擦除則無濟於事。 重要的是您是否需要從向量中刪除多個元素。 在這種情況下, std::remove將僅將每個未刪除的元素復制一次到其最終位置,而vector::erase方法會將所有元素從該位置多次移動到末尾。 考慮:

std::vector<int> v{ 1, 2, 3, 4, 5 };
// remove all elements < 5

如果逐個遍歷矢量刪除元素,則會刪除1,導致其余元素的副本移位(4)。 然后,您將刪除2並將所有剩余元素移動一(3)...如果看到模式,則這是O(N^2)算法。

std::remove的情況下,該算法將維護讀寫頭,並在容器上進行迭代。 對於前四個元素,將移動讀取頭並測試該元素,但不復制任何元素。 僅對於第五個元素,該對象將從最后一個位置復制到第一個位置,並且算法將完成一個副本並將迭代器返回到第二個位置。 這是一個O(N)算法。 后面帶有范圍的std::vector::erase將導致破壞所有其余元素並調整容器的大小。

正如其他人提到的那樣,在標准庫中,算法被應用於迭代器,並且缺乏對要迭代的序列的了解。 這種設計比其他算法可以識別容器的方法更加靈活,因為該算法的單個實現可以與符合迭代器要求的任何序列一起使用。 例如,考慮一下std::remove_copy_if ,即使使用無容器,也可以使用生成/接受序列的迭代器來使用它:

std::remove_copy_if(std::istream_iterator<int>(std::cin),
                    std::istream_iterator<int>(),
                    std::ostream_iterator<int>(std::cout, " "),
                    [](int x) { return !(x%2); } // is even
                    );

單行代碼將過濾掉標准輸入中的所有偶數,並將其轉儲到標准輸出,而無需將所有數字加載到容器的內存中。 這是拆分的優點,缺點是算法無法修改容器本身,只能修改迭代器引用的值。

std::remove是STL中的一種算法,與容器無關。 它需要一些概念,這是正確的,但已設計為還可以使用大小固定的C數組。

std::remove簡單地返回一個新的end()迭代器,以指向最后一個未刪除的元素之后的元素(從返回值到end()的項目數將與要刪除的項目數匹配,但是沒有確保它們的值與您要刪除的值相同-它們處於有效但未指定的狀態)。 這樣做是為了使其可以用於多種容器類型(基本上是ForwardIterator可以迭代通過的任何容器類型)。

調整大小后, std::vector::erase實際上會設置新的end()迭代器。 這是因為vector的方法實際上知道如何調整其迭代器(可以使用std::list::erasestd::deque::erase等來完成此操作)。

remove組織給定的容器以刪除不需要的對象。 容器的擦除功能實際上處理容器需要完成的“刪除”操作。 這就是為什么它們是分開的。

我認為這與需要直接訪問向量本身有關,以便能夠調整其大小。 std :: remove僅具有訪問迭代器的權限,因此它無法告訴向量“嘿,您現在的元素更少了”。

請參閱yves Baumes關於為何以這種方式設計std :: remove的答案。

是的,這就是要點。 請注意,其他erase性能也有所不同的其他標准容器也支持erase (例如list :: erase為O(1)),而std::remove則與容器無關,並且可以與任何類型的正向迭代器一起使用 (因此它也適用於裸陣列)。

的種類。 諸如remove的算法在迭代器(代表集合中元素的抽象)上工作,這些迭代器不一定知道它們在操作哪種集合類型,因此無法調用集合中的成員進行實際的刪除。

這很好,因為它允許算法在任何容器上以及在整個集合的子集范圍內通用地工作。

而且,正如您所說,為了提高性能,如果您需要的只是訪問邏輯端點位置並傳遞給其他算法,則實際上不必刪除(並銷毀)元素。

標准庫算法對序列進行操作。 一個序列是由一對迭代器定義的。 第一個指向序列中的第一個元素,第二個指向序列中的最后一個。 就這樣; 算法不在乎序列的來源。

標准庫容器保存數據值,並提供一對迭代器,這些迭代器指定算法使用的序列。 它們還提供成員函數,這些成員函數可以利用容器的內部數據結構來更有效地執行與算法相同的操作。

嘗試以下代碼以獲得更好的理解。

std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
const auto newend (remove(begin(v), end(v), 2));

for(auto a : v){
    cout << a << " ";
}
cout << endl;
v.erase(newend, end(v));
for(auto a : v){
    cout << a << " ";
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM