【问题标题】:Sorting one element in a sorted container对已排序容器中的一个元素进行排序
【发布时间】:2016-03-16 16:30:10
【问题描述】:

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

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

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

【问题讨论】:

  • 为什么不在找到位置后移动其余部分?

标签: c++ sorting


【解决方案1】:

理论上,您应该使用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) 范围内不小于(即大于或等于)值(参见cppreference)的第一个元素的迭代器;它仅适用于已分类的容器。它的复杂度在std::distance(beg, end)中是对数的。

std::vector&lt;T&gt;::insert(it, value) 将在it 之前插入value

在实践中(对于数量相对较少的元素),您最好使用线性搜索直到插入点,然后使用std::insert。换句话说:线性搜索是O(N)std::lower_boundO(log N),但是对于大约 10-50 个元素的向量(您的里程可能会有所不同),线性搜索更快(由于缓存效应)。

【讨论】:

    【解决方案2】:
    • 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);
        }
    }
    

    Demo

    std::rotate vs remove/insertion的优势在于复制/移动的数量: 如果您只需要交换前 2 个元素,std::rotate 只进行交换,而删除所有元素,并在另一侧再次插入元素。

    【讨论】:

      【解决方案3】:

      如果向量已经排序,你可以很容易地通过二分法找到更新值的新位置(与size+2处的值比较并迭代......)。然后你在新旧位置之间转移所有元素。

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

      【讨论】:

      • 完全正确,顺便说一句,使用相同的循环
      猜你喜欢
      • 2014-03-02
      • 1970-01-01
      • 2017-01-07
      • 1970-01-01
      • 2020-04-22
      • 2015-08-23
      • 2019-09-01
      • 2020-05-24
      • 1970-01-01
      相关资源
      最近更新 更多