一个不一样的案例
上一章我们说到了什么是复杂度,怎样计算复杂度。但是之前提到的都是类似于以下案例的:
int cal(int n) {
int sum = 0;
int j = 1;
for (int i = 0; i <= n; i++) {
sum = sum + i;
}
return sum;
}
这个案例中,时间复杂度是O(n),但是实际开发中会遇到更复杂的代码,比如像这样的,用之前的分析方式就有点难办了。
int find(int[] array, int target, int n) {
target = -1;
for (int i = 0; i <= n; i++) {
if(array[i] == target){
return array[i];
}
}
return target;
}
这个案例中,有不同的情况
1.有可能target不在数组array中,那么循环就是n次,时间复杂度与上一个案例无异,为O(n)。
2.有可能target在数组array中,那么循环就会在某个下标处中断,时间复杂度肯定就低于O(n)。
像这段代码中,时间复杂度有多种情况的,按照之前的分析方法不能分析出该案例的复杂度。
所以复杂度分析又分为最好情况时间复杂度,最坏情况时间复杂度,平均情况时间复杂度,均摊时间复杂度。
两个极端情况复杂度
最好情况时间复杂度和最坏情况时间复杂度分析都很简单,拿上个例子来说,最好情况就是target在数组array中,那么代码运行次数并不需要随n的增长而增长,即时间复杂度为O(1),最坏情况是target不在数组中,整个代码循环n次,代码运行次数随n的增长而增长,时间复杂度为O(n)。
那么两个极端情况的中间点呢,就是平均时间复杂度,这里平均时间复杂度是一个概率计算,target这个数有可能在数组array下标0~(n-1)的任意位置,这里是n种情况,还有一种不在数组中的情况,所以一共有(n+1)种情况。而程序运行次数与n的大小相关,这里要推导n+1种情况总运行次数是多少?
总的情况数,显而易见是,,而总的情况是(n+1),所以概率是:.
根据上一章的介绍,低阶常量都可以略去,那么时间复杂度就是O(n),即平均时间复杂度为O(n)。
但是之前的计算中并没有加上各种情况发生的概率,数target不在数组array 中的概率是,而在数组中的每一个位置的概率是。
经过计算
最终略去低阶和常量,时间复杂度还是O(n)。只不过这个复杂度叫做加权平均时间复杂度。
均摊又是什么鬼?
聚合分析,核算法,势能法??这些个摊还分析的方法又是什么晦涩难懂的词汇?作为一个程序员,不想了解那么多。
什么是均摊?举个例子,你和你女朋友玩个游戏,你女朋友在你身后画了25个大方格,每个方格都是同等大小,你手里有一个小石块,往后随手一仍。游戏规则是,你仍到O,会被踢屁股十下,仍到X,可以亲亲一下。那么,看这个图,你认为你每仍一下小石块被踢屁屁十下的可能性很大吗?虽然仍到O会被踢十下,但是大概率你是仍不到O的。
摊还分析是用来评价程序中的一个操作序列的平均代价,有时可能某个操作的代价特别高(丢到O了,囧),但是均摊到每次操作,也没有那么糟糕(大多数情况还是?),总体来说的均摊情况还是可以接受。
这画的是啥呀?这画能解决我计算复杂度吗?摊还分析还真不用计算复杂度,只要确定了最坏情况是极个别情况,摊还分析的时间复杂度就是最好情况时间复杂度!这么说,可以么?
当然这样的说法是不合学术的,只不过博主认为非要把一个知识用晦涩难懂的词汇描绘一遍没有意义。程序员要学的还很多,等到时机成熟在理解含义的基础上再来补充学术也为时不晚!
总结:
最好情况时间复杂度:代码在最理想情况下执行的时间复杂度;
最坏情况时间复杂度:代码在最坏情况下执行的时间复杂度;
平均情况时间复杂度:代码在所有情况下执行的次数除以所有情况数得到的表达式计算出的时间复杂度;
均摊时间复杂度:大多数情况的复杂度是什么,均摊时间复杂度就是什么!
本系列将每月更新,欢迎大家一同学习进步!