簡體   English   中英

在矢量上remove_if然后擦除有效嗎?

[英]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)中發生。

但是擦除將如何工作?

  • 如果擦除嘗試從X刪除到myVector.end(),則它將為O(n ^ 2),因為它將導致將向量復制到新位置,並且堆中將有O(n)個新分配。
  • 但是,如果將其從myVector.end()刪除為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因為最后兩個元素所使用的內存仍然存在,但沒有使用。

vector::erase的復雜性是明確定義的

線性:調用T的析構函數的次數與擦除的元素數相同,調用T的賦值運算符的次數等於擦除的元素之后向量中的元素數

它在內部的工作方式(即刪除元素的確切方式)是無關緊要的。

定義remove_if的復雜度,並且它是

謂詞的完全std :: distance(first,last)應用。

因此,您的代碼具有線性復雜度。

為什么不使用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.

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