问题描述
任何算法的最佳情况复杂度是算法完成其任务所需的最少时间。我们知道合并排序、快速排序等算法的最佳情况复杂度是Ω(n log(n)),它定义了这些算法的下界。
正如我们所知,在渐近符号中 -
O(n) + O(n log(n)) = O(n log(n))
还有,
Ω(n) + Ω(n log(n)) = Ω(n log(n))
所以,如果在这些排序算法中,我们首先在 O(n) 时间内遍历整个数组以确定数组是否已经按升序或降序排序,然后渐近它们的平均情况和最坏情况复杂度将保持不变.但是他们最好的情况复杂度现在变成了Ω(n)。
从逻辑上讲,我理解这些渐近符号的方式肯定存在缺陷,否则当渐近符号被开发或流行来衡量排序算法时,肯定有人会指出这一点。我是否正确地假设这是渐近符号中的一个似是而非的缺陷,或者我是否缺少渐近符号的一些规则?
解决方法
使用渐近复杂性作为速度的度量肯定存在问题。首先,显然常量确实很重要。 1000n
通常比 n log n
大得多,对于 n^1000
的任何实际值,2^n
肯定比 n
大得多。然而,事实证明,渐近复杂度通常是算法实际速度的一个很好的指标。
你提出的问题也是对的,但我认为不是问题。确实,在快速排序开始时进行简单的 isSorted()
检查将其最佳案例复杂度降低到 Θ(n)
,但人们很少关心最佳案例性能。事实上,许多常见问题的算法都可以修改为最佳情况线性,但这并不是很有用。
最后,请注意,这并不是渐进符号的真正缺陷。进行随机猜测并验证猜测是否正确(例如通过猜测数组已经排序)通常确实可以提高最佳情况的性能,而无论使用何种符号,对平均或最坏情况的影响很小。
,首先,您应该在头脑中区分情况(最佳、最差、平均等)和界限(上限、下限、O、Omega、Theta 等)
让我们关注冒泡排序,定义如下:
if array == null or array.length < 2 then return
do
swapped = false
for i = 0 to array.length - 2
if array[i] > array[i+1] then
swap(array,i,i+1)
swapped = true
until not swapped
该算法的最佳情况是排序数组,在这种情况下,下限 (Omega)、上限 (O) 和 Theta 边界都同意运行时受 f(n) = an 形式的函数约束;也就是说,T(n) = O(n)。冒泡排序的最佳情况是线性的。
这个算法最坏的情况是一个反向排序的数组。在这种情况下,运行时间由 g(n) = bn^2 之类的函数从上下限界;在最坏的情况下,T(n) = O(n^2)。
您没有遗漏任何东西,算法具有不同的最坏情况和最佳情况运行时界限是非常正常的。一个算法也完全有可能不会针对最好的情况进行优化,因为最好的情况通常不是我们所担心的;是的,归并排序可以首先检查数组是否已排序,但在长度为 N 的所有可能数组的集合中,这些数组的数量相对较少。
此外,您可以选择谈论最坏情况的下限或最佳情况的上限。这些东西不是我们通常关注的 - 而是关注最坏情况的上限,或者可能是最好情况的下限 - 但情况和界限是完全独立的事情,可以任意组合。