如何有效地从排序的向量中擦除值
How to erase a value efficiently from a sorted vector?
假设vec
是可移动和可复制对象的排序向量。删除所有与value
匹配的元素的最有效方法是什么?
这是正确和最有效的方法吗?
auto lb = std::lower_bound(vec.begin(), vec.end(), value);
vec.erase(lb, std::upper_bound(std::next(lb), vec.end(), value));
复杂性是什么?(考虑到删除后所需的任何移动(。
我已经用三种四种不同的方法从排序的容器中擦除进行了一些简短的测试。
void erase_v1(std::vector<int> &vec, int value)
{
vec.erase(std::remove(std::begin(vec), std::end(vec), value), std::end(vec));
}
void erase_v2(std::vector<int> &vec, int value)
{
auto lb = std::lower_bound(std::begin(vec), std::end(vec), value);
if (lb != std::end(vec) && *lb == value) {
auto ub = std::upper_bound(lb, std::end(vec), value);
vec.erase(lb, ub);
}
}
void erase_v3(std::vector<int> &vec, int value)
{
auto pr = std::equal_range(std::begin(vec), std::end(vec), value);
vec.erase(pr.first, pr.second);
}
// Surt's code, doesn't preserve sorted order
void erase_v4(std::vector<int> &vec, int value)
{
// get the range in 2*log2(N), N=vec.size()
auto bounds = std::equal_range(vec.begin(), vec.end(), value);
// calculate the index of the first to be deleted O(1)
auto last = vec.end() - std::distance(bounds.first, bounds.second);
// swap the 2 ranges O(equals) , equal = std::distance(bounds.first, bounds.last)
std::swap_ranges(bounds.first, bounds.second, last);
// erase the victims O(equals)
vec.erase(last, vec.end());
}
使用10,000,000个元素的std::vector
进行测试,填充[0..9]
范围内的随机数,然后排序(MS Visual C++ 2013(。
擦除值0
(容器正面(,代表时间如下所示:
time=14.3894 size=8999147 // v1, milliseconds and updated container size
time=11.9486 size=8999147 // v2
time=11.5548 size=8999147 // v3
time=1.78913 size=8999147 // v4 (Surt)
擦除5
(容器中间(:
time=12.8223 size=9000844
time=4.89388 size=9000844
time=4.87589 size=9000844
time=1.77284 size=9000844
擦除9
(容器的末端(:
time=12.64 size=9000820
time=0.00373372 size=9000820
time=0.00339429 size=9000820
time=1.29899 size=9000820
擦除13
(值不在容器中(:
time=11.8641 size=10000000
time=0.002376 size=10000000
time=0.00203657 size=10000000
time=0.00220628 size=10000000
erase/remove
方法始终循环访问整个容器,速度较慢,lower_bound/upper_bound
和 equal_range
方法在多次运行中几乎相同。我更喜欢最新版本,因为它是正确的,更简单的代码,并且键入更少。
编辑:根据请求定时Surt的代码。它始终如一地快速,但代价是不保留排序顺序。
擦除后使矢量保持未排序的解决方案。
// get the range in 2*log2(N), N=vec.size()
auto bounds=std::equal_range (vec.begin(), vec.end(), value);
// calculate the index of the first to be deleted O(1)
auto last = vec.end()-std::distance(bounds.first, bounds.last);
// swap the 2 ranges O(equals) , equal = std::distance(bounds.first, bounds.last)
std::swap_ranges(bounds.first, bounds.last, last);
// erase the victims O(equals)
vec.erase(last, vec.end());
std::remove
是 O(N(,此解决方案的写入次数也最少。如果等于接近 N,这可能不是一个好主意:)
在value
实际上没有出现在vec
的情况下,这是不正确的。所以至少你必须做:
auto lb = std::lower_bound(vec.begin(), vec.end(), value);
if (lb != vec.end() && *lb == value) {
vec.erase(lb, std::upper_bound(std::next(lb), vec.end(), value));
}
至于最有效的问题:我相信一般情况下,对vec
发生的事情一无所知,是的。复杂性仍然O(N)
erase()
因为O(N)
- 如果你像第二个元素一样擦除,你就不能真正进行非线性擦除。但就找到要擦除的界限而言,O(log N)
是最好的,你得到了它。
对于第二部分来说,upper_bound()
还是只是find_if()
更好,这个问题完全取决于你有很多value
的可能性。更有可能拥有很多,使用upper_bound()
,更有可能是唯一的,使用find_if()
。
- 为什么我的向量::擦除调用会抛出"vector subscript out of range"?
- 分割错误:向量中的擦除功能
- 如何使用擦除和迭代器来删除二维向量中的项目
- 从向量和对向量中擦除元素的差异
- 如何将某个元素从shared_ptr擦除到对象向量?
- 如何根据某些值擦除/删除类对象的向量
- 给定一个向量,擦除低于 itemsnum 的元素
- C++ 向量::使用类对象迭代器擦除不擦除向量成员
- C 矢量元素擦除与新向量创建
- 从向量中擦除元素是否也会擦除元素中存在的所有成员向量
- 向量擦除功能无法正常工作
- 使用迭代器时遇到问题,每 M 次传递一次擦除一个向量项
- 如何更有效地从向量或集合中擦除元素?
- 插入排序与插入/擦除?(所以我需要对一个向量进行排序,而不是手工做事,而是使用插入和擦除
- 使用擦除-删除范例将元素从一个向量移动到另一个向量
- 向量模板类:擦除(迭代器开始,迭代器端)
- remove_if然后擦除对向量有效吗
- 如何使用擦除删除习语从结构向量中删除值
- 使用索引向量擦除另一个向量的索引
- C++:向量::擦除用迭代器调用