检查向量是否已排序的最佳算法

18

如何最好地检查一个std::vector是否已排序?除了循环检查v[i]<=v[i+1],还有更快的方法吗?使用迭代器更快/更清晰吗?或者每次都调用sort是否更好(尽管“v已经排序”情况相当常见)?

我们可以安全地假设向量仅包含POD,通常为float,有时为doubleint

向量的大小不是微不足道的(通常是几千个项),但并不极端(不是千兆字节级别的)。

  • 在某些情况下,我们会立即对向量进行排序,但是在算法的错误案例中,我们也会有其他情况不需要对其进行排序。
  • 我们已经尽可能使用标志“IsSorted”。

如果尚未排序,你会立即进行排序吗?这可能会产生一些差异。 - crashmstr
13个回答

28

有比循环检查v[i]<=v[i+1]更快的方法吗?

没有。

如果您需要经常检查这个条件,可以创建一个包装类,该类保持“已排序”标志,初始值为False。当添加项时,将其设置为False,并添加一个成员函数sort(),在排序后将该标志设置为True。


这是个好主意!我们已经尽可能地这样做了:)。但在这种情况下,我们还是被困在老旧的vanilla std::vector中... - rlerallut
哦,太好了……当我看到这个问题时,这就是我想到的……很高兴我似乎想对了。 - John

25

最好的方法是使用std::is_sorted:

is_sorted(v.begin(), v.end())

:-)


2
那是SGI的扩展。我在GCC中有它,但在VC++7中没有。没错,这只是一次复制粘贴而已!无论如何,我将不得不对其基于迭代器的方法和基于索引的方法进行基准测试...然后我们就能说它是“最好的方法”了。 :) - rlerallut
6
C++11现在也有is_sorted函数。 - user283145

16

考虑多个CPU核心

这取决于您的平台和向量中的项数。您需要进行基准测试以找到最佳方案。

无法回答:“是否有比检查v[i]<=v[i+1]的循环更快的东西?”
答案是:没有。

因为……现代计算机有多个CPU /核心/超线程技术。因此,利用计算机中的并行性将检查工作分割成几个线程,每个CPU可以并行检查一个小范围,可能会更快。

最好使用库函数而不是自己实现。新版本的库将利用并行性。因此,如果您选择std::sort,则在构建针对STL的更新实现时,它们将为您并行执行操作,而无需担心。我不知道是否已经有现成的STL版本可以做到这一点,但坚持使用库函数是值得的,这样当您升级到具有此优化的版本时,您可以免费获得此优化,而无需进行任何更改。


+1 有见地。不幸的是,我的is_sorted实现(来自相当旧的gcc版本(3.4.x))非常基础。此外,对于这样一个简单的循环,内存带宽不是限制因素吗? - rlerallut
很难确定内存带宽是否会成为瓶颈。这完全取决于您的平台、向量大小、向量中项目的大小、比较所需时间等。这就是为什么如果您想找出如何挤出每一滴性能,就需要进行定时/基准测试。 - Scott Langham

12
std::adjacent_find(v.begin(), v.end(), std::greater<type>()) == v.end()

6
当然,我不了解你的问题领域,如果我说的内容不相关,请忽略我,但是在我看来,如果我需要经常按照排序方式访问集合,那么一个自然无序的集合,如vector<T>可能不是最好的选择。

1
传统格式以及数据容量。此外,一旦排序完成(这不是那么长的过程),访问向量非常快!但我赞赏你的思考方式“我是否正在解决正确的问题?” - rlerallut

5

有没有比循环检查v[i]<=v[i+1]更快的方法?

您需要检查任何值以查看其是否已排序,因此除非在变异向量时跟踪更改或使用已经排序的数据结构,否则它不会更快,最坏情况下为O(n)。

或者每次都调用sort实际上更好(尽管“v已经排序”的情况相当常见)吗?

请记住,当列表已经排序时(并且选择的枢轴不正确时),快速排序的最坏情况是发生。为了避免这种情况,您可能希望尝试std::stable_sort作为替代品。


std::sort 通常不是一个简单的快速排序算法。 - jfs

2

C++-11中的<algorithm>库包含了is_sorted函数。


2
如果您期望列表非常接近排序,最好尝试对插入排序进行修改。如果列表已经排序,它只需要一次遍历就可以告诉您。如果列表非常接近排序,它将非常快速地完成排序。如果列表未排序,在进行一定数量的交换后退出排序,并切换到快速排序(或stable_sort)。

1
有比检查v[i]<=v[i+1]的循环更快的方法吗?
没有。
但是,如果您要执行检查以决定是否对向量进行排序,则最好始终排序if您使用正确的排序算法,即std::stable_sort而不是std::sort。

嗯...对于POD类型,我认为stable_sort与std::sort使用的introsort相比没有任何优势。 - rlerallut
@rlerallut:具体的算法取决于库的实现。 - Jasper Bekkers
当列表已经排序时,您也可以使用插入排序来获得O(n)的最佳情况。如果超过一定数量的交换,则放弃该方法并使用快速排序。 - Eclipse

0
如果您在插入项目时使用二分查找来查找插入点,那么它永远不会无序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接