对已排序容器中的一个元素进行排序

Sorting one element in a sorted container

本文关键字:排序 一个 元素      更新时间:2023-10-16

我有一个排序对象的向量。在某些时候,其中一个对象被更新,并且该元素(仅)必须再次排序。

最好的方法是什么?std::sort()在整个容器上(如果容器已经排序,它更快吗?)或擦除()对象并将其插入()它应该在的位置?

编辑:在这种情况下,我必须使用矢量容器

理论上,你应该使用 std::lower_boundstd::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)中的第一个元素,该元素不小于(即大于或等于)值(参见 cpp首选项);它仅适用于排序的容器。它的复杂性在std::distance(beg, end)中是对数的。

std::vector<T>::insert(it, value)将在it之前插入value

在实践中(对于相对较少的元素),最好使用线性搜索直到插入点,然后std::insert。换句话说:线性搜索是O(N)的,std::lower_boundO(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::rotateremove/insertion的优势在于复制/移动的数量:如果您只需要交换前 2 个元素,std::rotate 只执行交换,而删除 shift 所有元素,并在另一侧再次插入 shift 元素。

如果向量已经排序,您可以通过二分法轻松找到更新值的新位置(与 size+2 的值进行比较并迭代......然后你在旧地方和新地方之间移动所有元素。

这是一种相当优化的方式,因为新旧地方之间的转换是一个结合std::erasestd::insert的浓缩操作,但编写和测试会更乏味......

相关文章: