复杂度分析的4个概念
1.最坏情况时间复杂度:代码在最理想情况下执行的时间复杂度。
2.最好情况时间复杂度:代码在最坏情况下执行的时间复杂度。
3.平均时间复杂度:用代码在所有情况下执行的次数的加权平均值表示。
4.均摊时间复杂度:在代码执行的所有复杂度情况中绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上。基本上均摊结果就等于低级别复杂度。
为什么要引入这4个概念?
1.同一段代码在不同情况下时间复杂度会出现量级差异,为了更全面,更准确的描述代码的时间复杂度,所以引入这4个概念。
2.代码复杂度在不同情况下出现量级差别时才需要区别这四种复杂度。大多数情况下,是不需要区别分析它们的。
平均情况时间复杂度
要考虑到执行结果的每种情况,并且计算出每种情况出现的概率。
最终计算出来的平均时间复杂度的值就是概率论中的加权平均值,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度或者期望时间复杂度。
均摊时间复杂度
在代码执行的所有复杂度情况中绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上(摊还分析法)。基本上均摊结果就等于低级别复杂度。均摊时间复杂度就是一种特殊的平均时间复杂度。
// array 表示一个长度为 n 的数组
// 代码中的 array.length 就等于 n
int[] array = new int[n];
int count = 0;
void insert(int val) {
if (count == array.length) {
int sum = 0;
for (int i = 0; i < array.length; ++i) {
sum = sum + array[i];
}
array[0] = sum;
count = 1;
}
array[count] = val;
++count;
}
它实现了一个往数组中插入数据的功能。当数组满了,也就是代码中count == array.length()时,我们遍历数组求和,并清空数组,将求和之后的sum值放到数组的第一位,然后再将新的数据插入,但如果数组一开始就有空闲空间,则直接将数据插入数组。
使用三种时间复杂度的分析方法来分析一下:
-
最理想情况:数组中有空闲空间,我们将数据插入到数组下标为count的位置就可以,那么时间复杂度为O(1)。
-
最坏情况:数组中没有空闲空间,我们需要将数组遍历求和,然后再将数据插入,那么时间复杂度为O(n)。
-
平均时间复杂度:根据加权平均计算为,为O(1)。
假设数组的长度是 n,根据数据插入的位置的不同,可以分为 n 种情况,每种情况的时间复杂度是 。除此之外,还有一种“额外”的情况,就是在数组没有空闲空间时插入一个数据,这个时候的时间复杂度是 。而且,这 种情况发生的概率一样,都是 。
摊还分析法来分析计算上面代码的时间复杂度:
每一次 的插入操作,都会跟着 次 的操作,所以把耗时多的那次操作均摊到接下来的 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 。
使用场景:
对一个数据结构进行一组连续操作中,大部分情况时间复杂度很低,只有个别情况下时间复杂度很高,而且这些操作存在前后连贯的时序关系,这种情况下,我们就要看是否能将较高的时间复杂度平摊到其他时间复杂度较低的操作上。在能够应用均摊时间复杂度的场合,一般均摊时间复杂度就等于最好情况的时间复杂度。
参考资料:
数据结构与算法之美(极客时间)链接:
http://gk.link/a/10435
GitHub链接:
https://github.com/lichangke/LeetCode
知乎个人首页:
https://www.zhihu.com/people/lichangke/
CSDN首页:
https://me.csdn.net/leacock1991
欢迎大家来一起交流学习