文章来源于极客时间前google工程师−王争专栏。
int cal(int n) {
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
return sum;
}
所有代码的执行时间T(n)与每行代码的执行次数n成正比

大O这种复杂度只是表示一种变化趋势。所以,我们在分析一个算法,一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。这段核心代码执行次数的n的量级,就是整段要分析代码的时间复杂度。
int cal(int n){
int ret = 0;
int i = 1;
for(;i<=n;++i){
ret = ret+f(i);
}
}
inf f(int n){
int sum = 0;
int i=1;
for(;i<=n;++i){
sum = sum+1;
}
return sum;
}
我们可以把乘法法则看成是嵌套循环。
O(1)只是常量级时间复杂度的一种表示方法,并不是指只执行了一行代码。如下代码,即便有3行,时间复杂度也是O(1),而不是O(3)
int i = 8;
int j = 10;
int sum - i+j;
一般情况下,只要算法中不存在循环语句、递归语句,即便有成千上万行的代码,其时间复杂度也为O(1)
对数时间复杂度非常常见,同时也是最难分析的一种时间复杂度。举例如下:
int i = 1
while(i <= n){
i=i*2;
}
从代码中可以看出,变量 i 的值从 1 开始取,每循环一次就乘以 2。当大于n时,循环结束。实际上变量i的取值就是一个等比数列

所以这段代码的时间复杂度就是O(log2n),通过换底公式,我们忽略对数的“底”,统一表示为O(logn),根据乘法法则,可以很容易理解O(nlogn),比如归并排序、快速排序的时间复杂度都是O(nlogn)
代码的复杂度由两个数据的规模来决定,代码如下:
int cal(int m,int n){
int sum_1 = 0;
int i = 1;
for(;im和n表示两个数据规模,我们事先无法评论m和n谁的量级大,所以我们在表示复杂度的时候,就不能简单的用加法法则,省略掉其中一个。所以上面代码的时间复杂度就是O(m+n)。但是乘法法则继续有效。
时间复杂度的全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。 类比一下,空间复杂度就是渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。
void pring(){
int i = 0;
int[] a = new int[n];
for(;i=0;--i){
print out a[j];
}
}
第二行代码中,我们申请了一个空间存储变量i,但是它是常量级别的,跟数据规模n没有关系,所以可以忽略。第三行申请了一个大小为n的int类型数组,除此之外,剩下的代码都没有占用更多的空间,所以整段代码的空间复杂度就是O(n)

有人说,我们项目之前都会进行性能测试,再做代码的时间、空间复杂度分析,是不是多此一举呢?每段代码都分析一下时间复杂度、空间复杂度,是不是很浪费时间呢?你怎么看待这个问题?
//n表示array数组的大小
int find(int[] array,int n,int x){
int i = 0;
int pos = -1;
for(int i=0;i你应该可以看出来,这段代码要实现的功能是,在一个无序的数组(array)中,查找变量x出现的位置。如果没有找到,就返回-1。这段代码的时间复杂度为O(n),我们在寻找的过程中如果提前找到了,可以提前结束,所以可以优化
int find(int[] array,int n;int x){
int i = 0;
int pos = -1;
for(;i那么问题就来了,优化后的代码时间复杂度还是O(n)吗?
不同情况下,这段代码的时间复杂度是不一样的。
所以需要引入三个概念:最好情况时间复杂度、最坏情况时间复杂度、平均情况时间复杂度。
最好:最理想情况下,执行这段代码的时间复杂度。
最坏:最糟糕的情况下,执行这段代码的时间复杂度。

平均时间复杂度为O(n)
这个结论是正确的,但是计算过程稍微有点问题,因为这n+1中情况,出现的概率并不是一样的。
假设在数组中与不在数组中的概率都为1/2,另外0n-1之间的概率一样,为1/n,所以根据概率乘法法则,要查找的数据出现在0n-1中任意位置的概率就是1/(2n)。
所以将每种情况发生的概率也考虑进去,那平均时间复杂度的计算过程就变成了这样:

这个值就是概率论中的加权平均,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度和期望时间复杂度。
均摊时间复杂度对应的分析方法,摊还分析(或者叫平摊分析)。
均摊时间复杂度应用的场景更加特殊,更加有限。
int[] array = new int[n];
int count = 0;
void insert(int val){
if(count == array.length){
int sum = 0;
for(int i=0;i概率论方法分析:
假设数组长度为n,可以分为n种情况,每种情况的时间复杂度为O(1),除此之外还有一种额外的情况,就是在数组没有空闲空间时插入一个数据,这个时候的时间复杂度为O(n)。而且这n中情况发生的概率是一样的,都是1/(n+1),所以根据加权平均的计算方法,所以平均时间复杂度为:

如何使用摊还分析来分析均摊时间复杂度呢?
每一次O(n)的插入操作,都会跟着n-1次O(1)的插入操作,所以把耗时多的那次操作均摊到接下来的n-1次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是O(1)。这就是均摊分析的大致思路。
均摊时间复杂度就是一种特殊的平均时间复杂度。
int[] array = new int[10];
int len = 10;
int i = 0;
void add(int element){
if(i>= len){
int[] new_array = new int[len*2];
for(int j=0;j