问题很简单,但我找不到足够好的答案。在the most upvoted SO question regarding the big-O notation上,它说:
例如,通常根据比较操作比较排序算法(比较两个节点以确定它们的相对排序)。
现在让我们考虑简单的冒泡排序算法:
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j+1]) {
switchPlaces(...)
}
}
}
我知道最坏的情况是O(n²)
,最好的情况是O(n)
,但究竟是什么n
?如果我们尝试对已经排序的算法进行排序(最好的情况),我们最终什么也不做,那为什么它仍然是O(n)
?我们仍在循环2个for循环,所以如果有的话,它应该是O(n²)
。 n
不能是比较操作的数量,因为我们仍然比较所有元素,对吗?
答案 0 :(得分:5)
在分析排序算法的Big-O性能时,n
通常表示您要排序的元素数量。
因此,例如,如果您使用冒泡排序对n
项进行排序,则最坏情况下的运行时性能将为O(n 2 )的顺序操作。这就是为什么冒泡排序被认为是一种极差的排序算法,因为它不能随着要排序的元素数量的增加而很好地扩展。随着要排序的元素数量线性增加,最坏情况的运行时间会逐渐增加。
这是一个示例图,演示了当问题大小N增加时各种算法如何根据最坏情况运行时进行扩展。深蓝色线表示线性缩放的算法,而品红色/紫色线表示二次算法。
请注意,对于足够大的N,二次算法最终需要比线性算法更长的时间来解决问题。
取自http://science.slc.edu/~jmarshall/courses/2002/spring/cs50/BigO/的图表。
答案 1 :(得分:5)
我认为这里有两件事情变得混乱,n
以及受到Big-O分析限制的n
的功能。
按照惯例,对于任何算法复杂度分析,n
是输入的大小,如果没有指定任何不同。对于任何给定的算法,输入大小有几个有趣的函数,可以计算asymptotic bounds,例如Big-O。
排序算法最常用的这种函数是最坏情况下的比较数。如果有人说排序算法是O(n^2)
,而没有指定其他任何内容,我会认为它们意味着最差情况下的比较计数为O(n^2)
,其中n
是输入大小。
另一个有趣的功能是除了要排序的数组之外的工作空间量,空间量。冒泡排序的工作空间为O(1)
,为常量空间,因为无论数组大小如何,它只使用一些变量。
冒泡排序可以编码为在最佳情况下仅进行n-1
数组元素比较,通过任何不进行交换的传递完成。请参阅此伪代码implementation,该代码使用swapped
来记住是否存在任何交换。如果数组已经排序,则第一遍不进行交换,因此排序在一次通过后结束。
答案 2 :(得分:3)
n
通常是输入的大小。对于数组,这将是元素的数量。
要查看不同的案例,您需要更改算法:
for (int i = arr.length - 1; i > 0 ; i--) {
boolean swapped = false;
for (int j = 0; j<i; j++) {
if (arr[j] > arr[j+1]) {
switchPlaces(...);
swapped = true;
}
}
if(!swapped) {
break;
}
}
您的算法的最佳/最差情况都是O(n^2)
,但有可能提前返回,最好的情况现在是O(n)
。
答案 3 :(得分:0)
n
是数组长度。您希望找到T(n)
算法复杂度。
访问内存然后检查条件是非常昂贵的。因此,您将T(n)定义为访问存储器的数量。
在给定的算法中,BC和WC使用O(n ^ 2)访问内存,因为你检查if条件O(n ^ 2)次。
提高复杂性:持有一个标志,如果你没有在主循环中进行任何交换,这意味着你的数组已经排序,你可以休息一下。
现在,在BC中,数组已经排序,您可以访问所有元素,因此O(n)。 在WC中仍然是O(n ^ 2)。