C++ 使用索引向量删除另一个向量的索引

C++ 使用索引向量删除另一个向量的索引,c++,vector,C++,Vector,我有两个向量,一个是另一个向量的索引向量,我想删除。目前我正在做以下工作: #include <vector> #include <iostream> #include <string> int main() { std::vector<std::string> my_vec; my_vec.push_back("one"); my_vec.push_back("two"); my_

我有两个向量,一个是另一个向量的索引向量,我想删除。目前我正在做以下工作:

#include <vector>
#include <iostream>
#include <string>

int main() {
        std::vector<std::string> my_vec;
        my_vec.push_back("one");
        my_vec.push_back("two");
        my_vec.push_back("three");
        my_vec.push_back("four");
        my_vec.push_back("five");
        my_vec.push_back("six");

        std::vector<int> remove_these;
        remove_these.push_back(0);
        remove_these.push_back(3);

        // remove the 1st and 4th elements
        my_vec.erase(my_vec.begin() + remove_these[1]);
        my_vec.erase(my_vec.begin() + remove_these[0]);

        my_vec.erase(remove_these.begin(), remove_these.end());

        for (std::vector<std::string>::iterator it = my_vec.begin(); it != my_vec.end(); ++it)
                std::cout << *it << std::endl;

        return 0;
}

当然,这是行不通的,因为
my_vec.erase()
要求迭代器引用同一个向量。

从标准序列中删除元素的已知惯用法是擦除/删除惯用法。首先调用
remove
算法,将要保留的所有元素移动到序列的前面,然后
erase
删除序列后面的元素。在C++11中,它如下所示:

std::vector< std::string > strings;
strings.erase(
    std::remove_if(
        strings.begin(), strings.end()
      , []( std::string const& s ) -> bool
        {
            return /*whether to remove this item or not*/;
        }
    )
  , strings.end()
);
std::vectorstrings;
字符串。删除(
std::如果需要,请删除(
strings.begin(),strings.end()
,[](std::string const&s)->bool
{
return/*是否删除此项*/;
}
)
,strings.end()
);
这将使用
remove\u if
和lambda函数,如果在向量
remove\u中找到当前元素的索引(由变量
计数器跟踪),该函数将返回true。对该向量进行排序,以便可以使用
二进制搜索
,作为优化。如果要删除的元素列表很小,则不对其进行排序可能会更快,只需在lambda中使用:

        return std::find(remove_these.begin(), remove_these.end(), counter++) != remove_these.end();

在你的情况下,我认为有两个问题值得考虑:

  • 您使用的是一个具有连续索引的容器,因此每次删除一个元素时,它之后的所有元素都会被重新索引(这就是为什么您必须在示例代码中按相反顺序进行删除的原因)
  • 该容器也会发生,因此任何删除都可能触发重新分配,并且至少提供元素的副本以满足连续性约束
考虑到这两个问题,在某些情况下,将您希望保留的元素复制到一个新的容器中,而不是执行删除操作可能会很有趣。在您的例子中,复制元素似乎不应该是一个大问题,因为许多
std::string
的实现都使用了a,但是您可能希望用自己的实现来验证这一点

另外要考虑的是,要删除的一组索引可以很好地存储在位向量中。它相当有效,并且大大简化了算法。不过,您需要跟踪删除的元素的有效数量

我个人会选择一个简单的循环,但是C++提供了很多方法来达到类似的结果。 以下是循环版本:

    std::vector<bool> remove_these(my_vec.size(), false):
    remove_these[0] = remove_these[4] = true;

    std::vector<std::string> my_result;
    my_result.reserve(my_vec.size() - 2);

    for (int i = 0; i < remove_these.size(); ++i)
        if (!remove_these[i])
             my_result.push_back(my_vec[i]);
就是这样。该算法的时间复杂度约为O(N+M),其中N和M是
my\u vec
remove\u这些
的大小。 或者,如果
,可以使用
remove\u替换第二个循环


事实上,如果stl提供了一个函数来迭代一个序列,比如
remove\u if
,并调用一个谓词函数,该函数的参数为该迭代器的键和值,那么我们可以通过向它提供
my\u vec
反向迭代器和lambda来检查给定的键是否在
remove\u this
中来使用它,但时间复杂度比上述解决方案略高。

您的删除标准是什么?你想删除随机元素吗?如果你想通过索引从一个向量中删除许多元素,也许用你想保留的元素来构建一个新的向量会更容易。@didierc:你应该对此做出回答。在我看来,当前的答案是不完整的,或者不能保证在所有实现上都能工作。@BenjaminLindley完成!如果您认为它值得重新调整,请告诉我(或进行编辑)。
remove\u这些
是一个
向量
,因此这不会像isSorry那样起作用,是的,这会起作用,假设
remove\u if
将按顺序调用您的元素。标准中是否有任何东西阻止
向量
中的
如果
从结尾到开头,则删除\u?事实上,它与前向迭代器一起工作。(从技术上讲,可以对向后工作的随机访问迭代器进行专门化,但这会有点奇怪。可并行的
remove\u if
可能不会线性工作。)嗯,向后工作可能有性能优势,因为您只移动已经访问过的元素,并且知道要保留的元素,您不会浪费时间移动稍后可能删除的元素。我得调查一下……这就是我问的时候的想法。我不确定标准对添加这种重载的实现有何规定……谢谢K-ballo。这是一个有用的解决方案。谢谢didierc。我改变了策略,现在使用“保留”方法而不是“删除”方法。特别感谢您的精彩讨论和解释!
        return std::find(remove_these.begin(), remove_these.end(), counter++) != remove_these.end();
    std::vector<bool> remove_these(my_vec.size(), false):
    remove_these[0] = remove_these[4] = true;

    std::vector<std::string> my_result;
    my_result.reserve(my_vec.size() - 2);

    for (int i = 0; i < remove_these.size(); ++i)
        if (!remove_these[i])
             my_result.push_back(my_vec[i]);
template <typename IT>
void erase(std::vector<std::string> &my_vec, IT begin, IT end){
    std::vector<std::string> res;
    std::vector<bool> r(my_vec.size(), false);
    res.reserve(my_vec.size() - (end - begin));
    for (IT it = begin; it != end; ++it)
        r[*it] = true;
    for (int i = 0; i < r.size(); ++i)
        if (!r[i])
            res.push_back(my_vec[i]);
    my_vec = res;
}