使用索引向量擦除另一个向量的索引
use a vector of indices to erase those indices of another 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_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;
}
但我认为这是不优雅和低效的。此外,我认为我必须小心地对我的remove_these
向量进行排序并从末尾开始(这就是为什么我在索引 0 之前擦除索引 3 的原因)。我想要一个擦除命令,类似于
my_vec.erase(remove_these.begin(), remove_these.end());
但这当然行不通,因为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::sort(remove_these.begin(), remove_these.end());
int counter = 0;
auto end = std::remove_if(my_vec.begin(), my_vec.end(),
[&](const std::string&) mutable {
return std::binary_search(remove_these.begin(), remove_these.end(), counter++);
});
my_vec.erase(end, my_vec.end());
这将remove_if
与 lambda 函数一起使用,如果在向量remove_these
中找到当前元素的索引(由变量counter
跟踪),则返回 true。对该向量进行排序,以便可以使用binary_search
作为优化。如果要删除的元素列表很小,则不对其进行排序并仅在lambda中使用它可能会更快:
return std::find(remove_these.begin(), remove_these.end(), counter++) != remove_these.end();
在你的情况下,我认为有两个问题值得考虑:
- 您使用的是具有连续索引的容器,因此每次删除元素时,都会重新索引它之后的所有元素(这就是您必须在示例代码中以相反顺序执行删除的原因),
- 该容器还恰好连续存储其元素,因此任何删除都可能触发重新分配,并且至少会引发元素的副本以满足连续性约束。
鉴于这两个问题,在某些情况下,将要保留的元素复制到新容器而不是删除元素可能会很有趣。在您的情况下,复制元素似乎不是一个大问题,因为许多std::string
实现都使用写入策略时复制,但您可能希望使用自己的策略进行验证。
要考虑的另一件事是,要删除的索引集可以很好地存储在位向量中。它相当高效,并显着简化了算法。不过,您需要跟踪删除的有效元素数。
我个人会选择一个简单的循环,但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]);
请注意,使用reserve
以避免在矢量填充过程中多次重新分配。
现在,剩下要做的就是将上面的代码包装在一个函数中,该函数将事先将 int 向量转换为布尔向量:
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;
}
就是这样。该算法的时间复杂度约为O(N+M),其中N和M是my_vec
和remove_these
的大小。 或者,可以用remove_if
替换第二个循环。
事实上,如果 stl 提供了一个函数来迭代像remove_if
这样的序列,并调用一个谓词函数,在参数中获取该迭代器的键和值,我们可以通过向它提供my_vec
反向迭代器和 lambda 来使用它来检查给定的键是否在remove_these
, 但是时间复杂度会比上面的解决方案高一点。
- 在C++中调整向量中的索引
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 将转换字符键入 int 以用作向量C++的索引
- 在对向量中查找元素的索引
- 如何检查类实例向量的索引是否为空
- 如何根据排序索引的向量对 std::index 集进行排序?
- C++ 中字符串向量的索引
- 如何在C++中返回向量的从零开始的索引
- 向量索引变量声明(size_t 或 std::vector<DATATYPE>::size_type)
- 特征获取索引数组,其中向量中的值为真(不需要循环)
- 引用向量中的特定索引
- 在C++中从距离矩阵创建索引向量的最快方法
- 在python gdb脚本中索引c ++向量
- 使用索引向量擦除另一个向量的索引
- 根据索引向量对向量重新排序 - 更新
- C++11将智能指针的索引向量作为类的成员
- 使用auto关键字填充双索引向量
- C++对不带lambda的索引向量进行排序
- 如何从Eigen中的索引向量中提取子向量(Eigen::Vector)
- 通过索引向量对犰狳矩阵的所有列进行排序的最佳方法