[英]Is remove_if and then erase efficient on vector?
盡管有關於向量的remove_if +擦除的數十個問題。 我找不到這種動作的效果。 當我寫:
myVector.erase(remove_if(myVector.begin(),
myVector.end(),
some_predicate), myVector.end());
remove if將使迭代器返回最后一個相關項+ 1(我們將其稱為X)。 我相信這將在O(n)中發生。
但是擦除將如何工作?
謝謝。
考慮這個向量:
|0|1|2|3|4|5|6|7|8|9|
我們使用remove_if
刪除所有4的倍數的元素:
std::remove_if(v.begin(), v.end(), [](auto i){ return i != 0 && !(i%4); });
這開始使用迭代器X遍歷向量,直到找到謂詞返回true的元素:
|0|1|2|3|4|5|6|7|8|9|
X
這是我們要刪除的第一個元素。
接下來,它創建另一個指向下一個元素Y = X + 1的迭代器,並檢查* Y的謂詞:
|0|1|2|3|4|5|6|7|8|9|
X Y
謂詞為false,因此我們希望保留該元素,因此通過執行*X = std::move(*Y)
將下一個元素分配給要刪除的元素:
|0|1|2|3|5|5|6|7|8|9|
X Y *X = std::move(*Y)
因此,我們有兩個迭代器X和Y,其中X指向“輸出”中的下一個元素(即,我們沒有刪除的元素),Y是下一個要考慮刪除的元素。
我們將兩個迭代器都移到下一個位置,檢查Y的謂詞(再次為假),然后執行另一次賦值:
|0|1|2|3|5|6|6|7|8|9|
X Y *X = std::move(*Y)
然后在下一個位置再次執行相同的操作:
|0|1|2|3|5|6|7|7|8|9|
X Y *X = std::move(*Y)
然后繼續前進,但發現該謂詞對Y是正確的
|0|1|2|3|5|6|7|7|8|9|
X Y
因此,它僅遞增Y,這會跳過該元素,因此不會將其復制到X的“輸出”位置:
|0|1|2|3|5|6|7|7|8|9|
X Y
謂詞對Y而言不正確,因此將其分配給X:
|0|1|2|3|5|6|7|9|8|9|
X Y *X = std::move(*Y)
然后它再次增加X和Y
|0|1|2|3|5|6|7|9|8|9|
X Y
現在,Y是末尾的,所以我們返回X(它指向輸出序列的末尾,即我們要保留的元素)。
在remove_if
返回X之后,我們調用v.erase(X, v.end())
,因此向量為從X到結尾的每個元素調用析構函數:
|0|1|2|3|5|6|7|9|~|~|
X end
然后設置大小,使向量以X結尾:
|0|1|2|3|5|6|7|9|
end
在此v.capacity() >= v.size()+2
因為最后兩個元素所使用的內存仍然存在,但沒有使用。
為什么不使用swap'n pop方法呢? 我們在優化向量的擦除方面無所適從,發現這是最快的,因為它具有O(1)
復雜度。 唯一的缺點是它不能保留順序。 在很多情況下都可以。 這是用於此操作的模板方法:
template<typename T>
inline typename std::vector<T>::iterator unorderedErase(std::vector<T>& p_container,
typename std::vector<T>::iterator p_it)
{
if (p_it != p_container.end() - 1)
{
std::swap(*p_it, p_container.back());
p_container.pop_back();
return p_it;
}
// else
p_container.pop_back();
return p_container.end();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.