【发布时间】:2016-03-16 16:30:10
【问题描述】:
我有一个已排序对象的向量。在某个时候,其中一个对象会更新,并且该元素(仅)必须再次排序。
最好的方法是什么?整个容器上的 std::sort() (如果容器已经排序,它更快吗?)或擦除()对象并插入()它应该在哪里?
编辑:在这种情况下,我必须使用矢量容器
【问题讨论】:
-
为什么不在找到位置后移动其余部分?
我有一个已排序对象的向量。在某个时候,其中一个对象会更新,并且该元素(仅)必须再次排序。
最好的方法是什么?整个容器上的 std::sort() (如果容器已经排序,它更快吗?)或擦除()对象并插入()它应该在哪里?
编辑:在这种情况下,我必须使用矢量容器
【问题讨论】:
理论上,您应该使用std::lower_bound和std::insert的组合,如下例所示:
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void insert(T value, std::vector<T>& data) {
auto it = std::lower_bound(data.begin(), data.end(), value);
data.insert(it, value);
}
template<typename C>
void show(const C& data) {
for(const auto & item : data) {
std::cout << item << " ";
}
std::cout << "\n";
}
int main() {
std::vector<int> data { 1, 2, 4, 5, 7, 9 };
show(data);
insert(3, data);
show(data);
insert(6, data);
show(data);
}
输出:
$ g++ example.cpp -std=c++14
./a.out
1 2 4 5 7 9
1 2 3 4 5 7 9
1 2 3 4 5 6 7 9
std::lower_bound(beg, end, val) 将找到指向[beg, end) 范围内不小于(即大于或等于)值(参见cppreference)的第一个元素的迭代器;它仅适用于已分类的容器。它的复杂度在std::distance(beg, end)中是对数的。
std::vector<T>::insert(it, value) 将在it 之前插入value。
在实践中(对于数量相对较少的元素),您最好使用线性搜索直到插入点,然后使用std::insert。换句话说:线性搜索是O(N),std::lower_bound 是O(log N),但是对于大约 10-50 个元素的向量(您的里程可能会有所不同),线性搜索更快(由于缓存效应)。
【讨论】:
std::sort 的复杂度为 O(n log n)。O(n)。O(log n) + O(length_of_rotation)。您可以使用std::lower_bound 查找新地点并使用std::rotate 修复订单:
void update(std::vector<int>& v, std::vector<int>::iterator old_it, int new_value)
{
if (old_it == v.end()) {
return;
}
auto it = std::lower_bound(v.begin(), v.end(), new_value);
*old_it = new_value;
if (old_it < it) {
std::rotate(old_it, old_it + 1, it);
} else if (it < old_it) {
std::rotate(it, old_it, old_it + 1);
}
}
std::rotate vs remove/insertion的优势在于复制/移动的数量:
如果您只需要交换前 2 个元素,std::rotate 只进行交换,而删除所有元素,并在另一侧再次插入元素。
【讨论】:
如果向量已经排序,你可以很容易地通过二分法找到更新值的新位置(与size+2处的值比较并迭代......)。然后你在新旧位置之间转移所有元素。
这是一种相当优化的方式,因为新旧位置之间的转换是结合std::erase和std::insert的浓缩操作,但是编写和测试会更加乏味...
【讨论】: