ChatGPT解决这个技术问题 Extra ChatGPT

是否有支持 insert() 等的 sorted_vector 类?

通常,使用已排序的 std::vector 而不是 std::set 更有效。有谁知道库类 sorted_vector,它基本上具有与 std::set 类似的接口,但将元素插入到排序的向量中(这样就没有重复),对 find 元素使用二进制搜索等?

我知道编写起来并不难,但最好不要浪费时间并使用现有的实现。

更新:使用排序向量而不是集合的原因是:如果您有数十万个小集合,每个集合仅包含 10 个左右的成员,那么仅使用排序向量会更节省内存。

我认为没有现成的课程。您可以自己编写或使用 lower_bound() 进行插入,使用 binary_search() 进行查找。
如果向量非常小,二分查找和顺序查找之间的差异也可能很小,因此您不妨只使用 std::vector。
由于集合会发生缓存未命中,差异可能会非常大。
@Frank:我对这个问题有点晚了,但无论如何:)你应该检查在“10个左右”元素的排序向量中的二进制搜索是否比线性搜索更快。它很可能不是更快,甚至可能更慢,因为处理器的分支预测将在这种情况下发挥重要作用。

E
Evgeny Panasyuk

Boost.Container flat_set

Boost.Container flat_[multi]map/set 容器是基于 Austern 和 Alexandrescu 指南的基于有序向量的关联容器。这些有序向量容器最近也受益于向 C++ 添加移动语义,大大加快了插入和擦除时间。平面关联容器具有以下属性: 比标准关联容器更快的查找比标准关联容器快得多的迭代。减少小对象的内存消耗(如果使用了 shrink_to_fit,则对于大对象) 提高缓存性能(数据存储在连续的内存中) 不稳定的迭代器(插入和擦除元素时迭代器无效) 不可复制和不可移动的值类型不能存储 比标准关联容器更弱的异常安全性(复制/移动构造函数在擦除和插入中移动值时会抛出) 比标准关联容器更慢的插入和擦除(特别是对于不可移动类型)

Live demo

#include <boost/container/flat_set.hpp>
#include <iostream>
#include <ostream>

using namespace std;

int main()
{
    boost::container::flat_set<int> s;
    s.insert(1);
    s.insert(2);
    s.insert(3);
    cout << (s.find(1)!=s.end()) << endl;
    cout << (s.find(4)!=s.end()) << endl;
}

jalf:如果你想要一个排序的向量,最好插入所有元素,然后在插入之后调用一次 std::sort() 。

boost::flat_set 可以自动做到这一点:

template<typename InputIterator> 
flat_set(InputIterator first, InputIterator last, 
         const Compare & comp = Compare(), 
         const allocator_type & a = allocator_type());

效果:使用指定的比较对象和分配器构造一个空集,并插入范围 [first, last) 中的元素。复杂性:如果范围 [first, last) 已经使用 comp 排序,则与 N 成线性关系,否则为 N*log(N),其中 N 是 last - first。


j
jalf

这样一个容器不是标准库的一部分的原因是它效率低下。使用向量进行存储意味着如果在向量中间插入了某些东西,则必须移动对象。在每次插入时都这样做会变得不必要地昂贵。 (平均而言,每次插入都必须移动一半的对象。这非常昂贵)

如果您想要一个已排序的向量,最好插入所有元素,然后在插入后调用 std::sort() 一次


我不明白这将如何解决问题。即使只是指针交换,仍然必须触摸所有对象。您仍在尝试做一些数据结构不适合的事情。
我开始写这样的答案,然后停下来,因为这根本不是真的。对于不到几十个元素,这真的很常见,平均移动一半很容易比执行分配和树重新平衡更便宜。当然最好调用一次 sort,我个人不会寻找容器来执行此操作,但这是风格问题。
对于 n 个元素,将 n 个元素插入到排序数组中是 log n 以找到插入点和 n/2 以移动现有元素。 O(nnlog n),根本没有效率。如果 n 足够小,可能会解决。
@Potatoswatter:用基于节点的数据结构替换它不是我建议的替代方案。就像你说的,堆分配和树重新平衡也变得昂贵(尽管自定义分配器可能会有所帮助)。最后,排序一次是我的建议。
我建议结合使用 std::map 和 std::vector 作为解决方案。
M
Michael Burr

我认为 STL 中没有“排序容器”适配器,因为已经有适当的关联容器来保持排序,几乎适用于所有情况。老实说,我能想到的拥有排序 vector<> 容器的唯一原因可能是与需要排序数组的 C 函数互操作。当然,我可能会遗漏一些东西。

如果您觉得排序的 vector<> 更适合您的需求(意识到将元素插入向量的缺点),这里是 Code Project 的实现:

一个符合 STL 的排序向量 作者:Martin Holzherr

我从来没有使用过它,所以我不能保证它(或它的许可证——如果有的话)。但是快速阅读这篇文章,看起来作者至少为容器适配器做出了很大的努力,以拥有一个合适的 STL 接口。

似乎值得仔细看看。


在集合变得相当大(100 个元素)之前,排序的向量可能会更快。集合具有可怕的缓存局部性。
B
Billy ONeal

如果您决定自己推出,您可能还想查看 boost:ublas。具体来说:

#include <boost/numeric/ublas/vector_sparse.hpp>

并查看坐标向量,它实现了值和索引的向量。此数据结构支持 O(1) 插入(违反排序),但随后按需排序 Omega(n log n)。当然,一旦它被排序,查找是 O(logn)。如果数组的一部分已排序,算法会识别这一点并仅对新添加的元素进行排序,然后进行就地合并。如果您关心效率,这可能是您能做的最好的事情。


M
Mooing Duck

Alexandresu 的 Loki 有一个排序的向量实现,如果你不想经历滚动你自己的相对微不足道的努力。

http://loki-lib.sourceforge.net/html/a00025.html


m
moodboom

Here 是我多年来一直在生产代码中使用的 sorted_vector 类。它具有重载,可让您使用自定义谓词。我已经将它用于指针容器,这在很多用例中都是一个非常好的解决方案。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅