目录
三个复杂度分析知识点
- 最好情况时间复杂度(best case time complexity)
- 最坏情况时间复杂度(worst case time complexity)
- 平均情况时间复杂度(average case time complexity)
最好、最坏情况时间复杂度
概念(是什么)
- 最好情况时间复杂度:在最理想的情况下,执行这段代码的时间复杂度
- 最坏情况时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度
为什么使用
以上两种复杂度对应的都是极端情况下的代码复杂大,发生的概率不大,学会分析即可。
如何分析(如何使用)
举个小栗子:
// n表示数组array的长度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for(; i < n; ++i) {
if(array[i] == x) pos = i;
}
return pos;
}
代码段解读:在一个无序的数组(array)中,查找变量 x 出现的位置。如果没有找到,就返回 -1。这段代码的复杂度是 O(n),其中,n 代表数组的长度。
在数组中查找一个数据,并不需要每次都把整个数组都遍历一遍,因为有可能中途找到就可以提前结束循环了。但是,这段代码写得不够高效。可以这样优化一下这段查找代码。
// n表示数组array的长度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for(; i < n; ++i) {
if(array[i] == x) {
pos = i;
break;
}
}
return pos;
}
代码优化后复杂度就不是 O(n) 了。因为,要查找的变量 x 可能出现在数组的任意位置。
如果数组中第一个元素正好是要查找的变量 x,那就不需要继续遍历剩下的 n-1 个数据了,那时间复杂度就是 O(1) —— 最好时间复杂度。
如果数组中不存在变量 x,那就需要把整个数组都遍历一遍,时间复杂度就成了 O(n) —— 最坏时间复杂度。
综上,不同的情况下,这段代码的时间复杂度是不一样的。
平均情况时间复杂度
概念(是什么)
最好情况时间复杂度和最坏情况时间复杂度对应的都是极端情况下的代码复杂度,发生的概率其实并不大,需要引入另一个概念:平均情况时间复杂度,简称为平均时间复杂度。
为什么使用
为了更好地表示平均情况下的复杂度
如何分析(如何使用)
举个小栗子
使用前面的代码段,要查找的变量 x 在数组中的位置,有 n+1 种情况:在数组的 0~n-1 位置中和不在数组中。我们把每种情况下,查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值,即:
时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,把刚刚这个公式简化之后,得到的平均时间复杂度就是 O(n)。
以上 n+1 种情况,出现的概率并不是一样的。要查找的变量 x,要么在数组里,要么就不在数组里。这两种情况对应的概率统计起来很麻烦,为了方便理解,假设在数组中与不在数组中的概率都为 1/2。另外,要查找的数据出现在 0~n-1 这 n 个位置的概率也是一样的,为 1/n。所以,根据概率乘法法则,要查找的数据出现在 0~n-1 中任意位置的概率就是 1/(2n)。
因此,前面的推导过程中存在的最大问题就是,没有将各种情况发生的概率考虑进去。如果把每种情况发生的概率也考虑进去,那平均时间复杂度的计算过程就变成了这样:
这个值就是概率论中的加权平均值,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度或者期望时间复杂度。
代码段复杂度平均值去除系数和常量后加权平均时间复杂度仍为 O(n)。
应用场景
在大多数情况下,并不需要区分最好、最坏、平均情况时间复杂度三种情况,使用一个复杂度就可以满足需求了。只有同一块代码在不同的情况下,时间复杂度有量级的差距,才会使用这三种复杂度表示法来区分。