擦除范围从std :: vector删除向量的最佳方法
Best way to erase vector of ranges from std::vector
在我的一个项目中,有必要从std::vector<double> values
中删除某些元素。我必须删除的索引作为间隔向量给出。例如,{1,3}
的意思是,我必须从values
中删除1至3个索引。
我可以假设给出的间隔是相互排斥的。
下面显示的代码说明了所需的行为应该是什么样的。
#include <iostream>
#include <vector>
int main(int argc, char** args) {
// Intervals of indices I have to remove from values
std::vector<std::pair<int, int>> intervals = { {1,3},{7,9},{13,13} };
// Vector of arbitrary values.
std::vector<double> values = {4.2,6.4,2.3,3.4,9.1,2.3,0.6,1.2,0.3,0.4,6.4,3.6,1.4,2.5,7.5 }
removeIntervals(values, intervals);
// intervals should contain 4.2,9.1,2.3,0.6,6.4,3.6,1.4,7.5
}
实现此目标所需的最短代码可能是多少?
到目前为止,我最好的解决方案是:
void removeIntervals(std::vector<double>& values, const std::vector < std::pair<int, int>>& intervals) {
std::vector<bool> flags(values.size(), true);
std::vector<double> ret;
for (auto interval : intervals) {
std:fill(flags.begin() + interval.first, flags.begin()+interval.second+1, false);
}
for (auto i = 0; i < values.size(); i++) {
if (flags[i]) ret.push_back(values[i]);
}
values = ret;
}
我可以假设,我的间隔是不重叠的和连续的。看来,它归结为从前到前的擦除。
void removeIntervals2(std::vector<double>& values, const std::vector < std::pair<int, int>>& intervals) {
auto revIntervals = intervals;
std::reverse(revIntervals.begin(), revIntervals.end());
for (auto interval : revIntervals) {
values.erase(std::begin(values) + interval.first, std::begin(values) + interval.second + 1);
}
}
,因为您可以假定间隔不会重叠并增加顺序,所以解决方案是在背面启动(以使索引不会更改)并删除每个范围转:
因此,对于最少的代码,您要求的代码:
for (auto& it = intervals.rbegin(); it != intervals.rend(); ++it) {
values.erase(values.begin() + it->first, std::next(values.begin() + it->second));
下面的一面是,这将涉及大量的矢量改组。的确,您想做的是在矢量末尾交换最后一个未换的物品,并使用要删除的项目,然后在完成时调整大小以切断末端;但这需要更多代码。
问题是非平凡的,因为在第一次呼叫vector::erase()
所有索引/迭代器之后,第一个删除的元素被无效,包括进一步的间隔要删除。
因此,必须按要删除元素的降序顺序完成使用vector::erase()
。
另一种不便源于使用int
索引而不是迭代器在间隔边界上。最后,vector::erase()
副本(矿石移动)所有元素都超过了最后删除的元素以填补空白。这保留了值的顺序,但在几个间隔的情况下会导致过度复制(移动)。
一种更有效的方法是仅交换要删除的元素,最后缩小矢量大小。
您肯定想要的解决方案不仅具有短代码,而且是良好的效率,可以最大程度地减少值的副本和变化。
我肯定会选择解决方案的第一部分,那就是要保留或删除的位置。
std::vector<bool> flags(values.size(), true);
for (auto interval : intervals) {
std:fill(flags.begin() + interval.first, flags.begin()+interval.second+1, false);
}
在第二部分中,最短,最有效的是erase/remove_if
成语:
values.erase(std::remove_if(begin(values), end(values),
[&](const auto& v) { return !flags[&v - &(*values.begin())];}),
values.end());
此处的效率是由于remove_if
将首先 Mark 需要删除的元素,然后它将通过将第一个元素保留并返回第一个元素的位置来压缩矢量去除。最后,erase
将收缩矢量。从算法的角度来看,该解决方案可能是最佳的。它应该为大量向量支付。
以为我会发布一个更容易容忍的答案。如果您的间隔大于输入阵列,例如,如果包括intervals
包含{15, 15}
,则此仍然可以正常运行。此外,这比Ukmonkey的解决方案更快,因为它可以在单个通行证中完成所有工作:
我已经注意到该代码已定义,并且仅在Clang和Visual Studio 2015 Update 3:
values.resize(distance(begin(values), remove_if(begin(values), end(values), [i = 0U, it = cbegin(intervals), end = cend(intervals)](const auto&) mutable { return it != end && ++i > it->first && (i <= it->second || (++it, true)); })));
实时示例
您可以在for
-Loop中完成相同的事情:
size_t write = 0U;
auto it = cbegin(intervals);
for (size_t read = 0U; read < size(values); ++read) {
if (it == cend(intervals) || read < it->first) {
values[write++] = values[read];
} else if (read == it->second) {
++it;
}
}
values.resize(write);
实时示例
如果您挂在"实现此目标所需的最短代码"上,则可以在for
-Loop中使用我的邪恶,
:
for (size_t read = 0U; read < size(values); ++read) if (it == cend(intervals) || read < it->first || (read == it->second && (++it, false))) values[write++] = values[read];
好吧,到目前为止的答案都是不好的 - 要么制造全新的向量,要么需要O(n^2)时间 - 所以我会添加这个。
,而不是删除您不想保留的元素,并每次移动其余的元素,而是将其移动 do 想要保持适当的位置,然后只是将矢量截断。
o(n)时间,没有额外的空间:
void removeIntervals(std::vector<double>& values, const std::vector < std::pair<int, int>>& intervals) {
if (intervals.size()<=0)
return;
//keep the part before the first interval
auto dest = values.begin()+intervals[0].first;
for (size_t i=0; i<intervals.size(); ++i) {
//copy the part to keep after each interval
auto s = values.cbegin()+intervals[i].second+1;
auto e = (i+i >= intervals.size() ?
values.cend() :
values.cbegin()+intervals[i+1].first);
while(s<e) {
*dest++=*s++;
}
}
values.erase(dest,values.end());
}
在补充马特·蒂默曼(Matt Timmermans)答案:这不是问题,但是如果您只想在c 17中仅保留值,则可以写下:
void remove_if_not_in_interval(std::vector<double>& result, const std::vector<std::pair<int,int> >& intervals)
{
if (intervals.size() == 0)
result.clear();
auto dest = result.begin();
for (auto [first, last] : intervals)
{
while(first!=last+1)
{
*dest++ = *(result.begin() + first++);
}
}
result.erase(dest,result.end());
}
- 选择和修改嵌套向量中的条目的最佳实践
- C++:实现向量和矩阵类的最佳结构
- C++:将向量传递到构造函数以创建成员变量的最佳方法?
- 在向量中查找大于 0(或通常为 k)的最小元素的最佳方法是什么?
- 从数组中删除非唯一值、保持顺序和不使用向量的最佳方法?
- 从列表/向量制作嵌套 for 循环的最佳方法是什么?
- 从整数向量向量搜索整数向量的最佳算法
- 查找字符串是否包含字符串向量的任何一个元素的最佳方法
- 将对象附加到智能指针向量的最佳方法
- 获得给定向量的排列索引列表的最佳方法是什么?
- 获取地图值作为向量的最佳方法是什么
- 哪种数据结构是在一个向量中搜索和计数对象对的最佳数据结构
- 擦除范围从std :: vector删除向量的最佳方法
- 最佳做法:将派生类的向量传递给基类向量上的方法
- 在std::向量中表示原始字节的最佳方式
- 从向量中获取n个最佳元素
- 存储动态创建对象的列表/向量的最佳方法是什么
- C++是修改这些对象的向量中所有对象的给定成员的最佳方法
- 将函数应用于向量子集的最佳方法是什么
- 在转换一个向量的元素时,连接两个向量的最佳方式是什么