簡體   English   中英

使用索引擦除 std::vector 中的元素

[英]Erasing elements in std::vector by using indexes

我有一個std::vector<int> ,我需要刪除給定索引處的所有元素(向量通常具有高維)。 我想知道,考慮到應該保留原始向量的順序,這是執行此類操作的最有效方法。

雖然,我找到了關於這個問題的相關帖子,但其中一些需要刪除一個元素多個元素,而remove-erase 習慣用法似乎是一個很好的解決方案。 但是,就我而言,我需要刪除多個元素,並且由於我使用的是索引而不是直接值,因此不能應用remove-erase idiom ,對吧? 我的代碼在下面給出,我想知道在效率方面是否可以做得更好?

bool find_element(const vector<int> & vMyVect, int nElem){
    return (std::find(vMyVect.begin(), vMyVect.end(), nElem)!=vMyVect.end()) ? true : false;
}

void remove_elements(){

    srand ( time(NULL) );

    int nSize = 20;
    std::vector<int> vMyValues;
    for(int i = 0; i < nSize; ++i){
            vMyValues.push_back(i);
    }

    int nRandIdx;
    std::vector<int> vMyIndexes;
    for(int i = 0; i < 6; ++i){
        nRandIdx = rand() % nSize;
        vMyIndexes.push_back(nRandIdx);
    }

    std::vector<int> vMyResult;
    for(int i=0; i < (int)vMyValues.size(); i++){
        if(!find_element(vMyIndexes,i)){
            vMyResult.push_back(vMyValues[i]);
        }
    }
}

我認為它可能會更有效,如果您只是對索引進行排序,然后從向量中從最高到最低刪除這些元素。 刪除列表中的最高索引不會使您要刪除的較低索引無效,因為只有高於已刪除索引的元素才會更改其索引。

如果它真的更有效將取決於排序的速度。 關於此解決方案的另一個優點是,您不需要值向量的副本,您可以直接在原始向量上工作。 代碼應如下所示:

... fill up the vectors ...

sort (vMyIndexes.begin(), vMyIndexes.end());

for(int i=vMyIndexes.size() - 1; i >= 0; i--){
    vMyValues.erase(vMyValues.begin() + vMyIndexes[i])
}

擦除 - 刪除給定索引處的多個元素

更新:在@kory 對性能的反饋之后,我修改了算法,不使用標記和移動/復制塊中的元素(不是一個接一個)。

筆記:
  • 索引需要排序且唯一
  • 使用std::move (替換為 c++98 的std::copy ):

Github實例

代碼:
std::vector<int> v = /*...*/; // vector to remove elements from
std::vector<int> ii = /*...*/; // indices of elements to be removed

// prepare indices
std::sort(ii.begin(), ii.end());
ii.erase(std::unique(ii.begin(), ii.end()), ii.end());

// remove elements at indices
v.erase(remove_at(v.begin(), v.end(), ii.begin(), ii.end()), v.end());
使用示例:
 std::vector<int> v = /*...*/; // vector to remove elements from std::vector<int> ii = /*...*/; // indices of elements to be removed // prepare indices std::sort(ii.begin(), ii.end()); ii.erase(std::unique(ii.begin(), ii.end()), ii.end()); // remove elements at indices v.erase(remove_at(v.begin(), v.end(), ii.begin(), ii.end()), v.end());

為了避免多次移動相同的元素,我們可以通過刪除索引之間的范圍移動它們

// fill vMyIndexes, take care about duplicated values
vMyIndexes.push_back(-1); // to handle range from 0 to the first index to remove
vMyIndexes.push_back(vMyValues.size()); // to handle range from the last index to remove and to the end of values
std::sort(vMyIndexes.begin(), vMyIndexes.end());
std::vector<int>::iterator last = vMyValues.begin();
for (size_t i = 1; i != vMyIndexes.size(); ++i) {
    size_t range_begin = vMyIndexes[i - 1] + 1;
    size_t range_end = vMyIndexes[i];
    std::copy(vMyValues.begin() + range_begin, vMyValues.begin() + range_end,   last);
    last += range_end - range_begin;
}
vMyValues.erase(last, vMyValues.end());

PS 修復了一個錯誤,感謝Steve Jessop耐心地向我展示

您可以做的是將向量(實際上是任何非關聯容器)分成兩組,一組對應於要擦除的索引,另一組包含 rest。

template<typename Cont, typename It>
auto ToggleIndices(Cont &cont, It beg, It end) -> decltype(std::end(cont))
{
    int helpIndx(0);
    return std::stable_partition(std::begin(cont), std::end(cont), 
        [&](typename Cont::value_type const& val) -> bool {
            return std::find(beg, end, helpIndx++) != end;
    });
}

然后,您可以從(或最多)分割點刪除以擦除(僅保留)與索引對應的元素

std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);

int ar[] = { 2, 0, 4 };
v.erase(ToggleIndices(v, std::begin(ar), std::end(ar)), v.end());
  • 如果不需要“僅按索引保留”操作,您可以使用 remove_if insted of stable_partition (O(n) vs O(nlogn) 復雜度)
  • To work for C arrays as containers the lambda function should be [&](decltype(*(std::begin(cont))) const& val) -> bool { return std::find(beg, end, helpIndx++);=結尾。 但隨后 .erase() 方法不再是一個選項

如果要確保每個元素只移動一次,則可以簡單地遍歷每個元素,將要保留的元素復制到新的第二個容器中,不要復制要刪除的元素,然后刪除舊容器並用新的替換它:)

這是一種基於Andriy Tylychko答案的算法,這樣可以更輕松、更快速地使用答案,而無需將其分開。 它還消除了在索引列表開頭添加 -1 並在末尾添加一些items的需要。 還有一些調試代碼,以確保indices有效(排序和有效索引進入items )。

template <typename Items_it, typename Indices_it>
auto remove_indices(
    Items_it items_begin, Items_it items_end
  , Indices_it indices_begin, Indices_it indices_end
)
{
    static_assert(
      std::is_same_v<std::random_access_iterator_tag
        , typename std::iterator_traits<Items_it>::iterator_category>
      , "Can't remove items this way unless Items_it is a random access iterator");

    size_t indices_size = std::distance(indices_begin, indices_end);
    size_t items_size = std::distance(items_begin, items_end);
    if (indices_size == 0) {
        // Nothing to erase
        return items_end;
    }

    // Debug check to see if the indices are already sorted and are less than
    // size of items.
    assert(indices_begin[0] < items_size);
    assert(std::is_sorted(indices_begin, indices_end));

    auto last = items_begin;
    auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) {
        std::copy(items_begin + range_begin, items_begin + range_end, last);
        last += range_end - range_begin;
    };

    size_t last_index = -1;
    for (size_t i = 0; i != indices_size; ++i) {
        shift(last_index + 1, indices_begin[i]);
        last_index = indices_begin[i];
    }
    shift(last_index + 1, items_size);
    return last;
}

這是一個使用示例:

template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T>& v)
{
    for (auto i : v) {
        os << i << " ";
    }
    os << std::endl;
    return os;
}

int main()
{
    using std::begin;
    using std::end;
    std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
    std::vector<int> indices = { 0, 1, 2, 3, 4 };

    std::cout << items;
    items.erase(
          remove_indices(begin(items), end(items), begin(indices), end(indices))
        , std::end(items)
    );
    std::cout << items;

    return 0;
}

Output:

1 3 6 8 13 17 
17 

所需的標題是:

#include <iterator>
#include <vector>
#include <iostream> // only needed for output
#include <cassert>
#include <type_traits>

可以在 godbolt.org 上找到Demo

暫無
暫無

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

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