谈谈算法的时间复杂度

问题一:我们如何判断一个算法的优劣?

答:在不考虑额外空间开销的情况小,对于同一个问题,直观而言,解决这个问题的算法需要的时间越少,我们就可以认为这个算法的效率越高。

就比如:如果我们需要在有序数组array中查找一个数key,返回这个key在数组中的下标,(我们假设这个key是在数组中一定存在的)。我们第一个反应是遍历整个数组。

for(int i=0;i<array.length;i++){
    if(array[i]==key)
    return i;
}

暴力求解,如此的简单,so easy.

但是如果我们这个数组里如果有一亿个数,那么在最坏情况下(比如这个数字在数组的末尾),我们需要遍历完整个数组才能找到这个数,那么我们需要的时间是非常大的,所以这个这个算法在数据量大的情况下显得不那么的友好。

所以我们通常不那么做,而是使用二分查找。(每次查找范围都缩小一半,知道找到这个数为止。)

public static int rank(int [] array,int key)
{//前提:数组有序
int low=0;
int height=array.length-1;
while(low<=height)
    {
    int mid=low+(height-low)/2 //数组的中间索引。
    if(key<array[mid]) { height=mid-1;}
    if(key>array[mid]) {low=mid+1;}
    else return mid;
    }


}

那么在最坏情况下,我们在这个数组中需要找打这个数,我们来计算一下查找的次数。第一次N/2,第二次N/2/2,第三次N/2/2/2

易得第m次N/2^m,所以找到这个数的最坏情况为(2^m=n,则m=log2(N))。

long(2N)的查找次数远远小于N。所以对于统一个问题,好的算法和坏的算法效率天壤之别。

 

我们既然知道,算法运行的快慢(时间的长短)是作为判断算法优劣的之一(今天只讲时间复杂度,还要考虑到空间复杂度),那么如何推广到一般性,即我们如何判断算法的执行时间呢?

问题二:如果我们需要知道一个算法执行的时间,我们应该怎么做。

解决一:直接用时间函数来统计,它根据计算机的不同,时间会不一样,但是我们如果想知道时间,每次都要实际操作才行,这样是不太现实的,有什么方法可以直观的得出一个算法的效率呢?直觉告诉我们它和问题的规模有关。

解决二,我们直接用数学模型来判断,我们根据执行语句执行的次数,来得出一个函数,这个函数就是问题的规模。这个问题的规模取决于最频繁的执行语句。显然算法运行时间是根据执行最频繁的语句的时间来决定的。

 

举个例子:

Int count=0;

For(int i=0;i<N;i++)

     count++;

这个算法(姑且称之为算法吧),for循环这条语句执行的次数为N。问题的规模也为N。

所以这个算法执行的时间取决于N。

 

例子二:三数之和:

public static int sum(int [] a)
{统计数组中和为0的三元组的数量。
int count=0;
int N=a.length;
for(int i=0;i<N;i++)
    for(int j=i+1;j<N;j++)
        for(int k=j+1;k<N;k++)
            if(a[i]+a[j]+a[k]==0)
                count++;

return count;

}

在这个嵌套的三层循环中,我们可以看到,If语句是执行最频繁的那条语句,我们选取它作为这个算法执行的次数,可得它的执行的频率为N(N-1)(N-2)/6=N^3/6-N^2/2+N/3。。这条式子在这里就推导,有兴趣可以自行推导。

求近似

但是如果我们按照这样的分析,用语句执行的频率来作为算法的时间复杂度。那么当产生复杂数学表达式的时候,不利于我们分析这个算法的优劣。例如:

N(N-1)(N-2)/6=N^3/6-N^2/2+N/3。

这个表达式是不是看起来有点复杂,但是我们可以知道的是,一般在这种表达式中,随着N的增大,那么首项之后其他项的值都相对较小,(例如当N=1000时,-N^2/2+N/3≈499 667,而N^3/6≈166 666 667,).由此我们可以得出,我们可以去除非常复杂但是幂次较低的项,因为它们对于我们的结果影响较小。

当N不断增大的时候,N^3/6-N^2/2+N/3除以N3/6的值无限趋近于一。所以我们用N^3/6作为近似值

所以我们取近似值N^3/6。

再进一步我们忽略系数,而把所有的注意力集中在问题规模上,那么这个算法的时间复杂度为N^3.。所以我们以后取时间复杂度时,只取最高次幂并且忽略它的系数。

 

 

函数

近似

时间复杂度

N^3/6-N^2/2+N/3

N^3/6

N^3

N^2/2+N/3

N^2/2

N^2

lgN+1

lgN

lgN

3

3

1

 

常见的时间复杂度总结

描述

时间复杂度

典型代码

说明

常数级别

1

a+b=c

普通语句

对数级别

logN

二分查找

二分策略

线性级别

N

For(int i=0;i<N;i++)

{ }

循环

线性对数级别

NLogN

分治

归并排序

平方级别

N2

For(int i=0;i<N;i++){

For(int j=i;j<N;j++) { }

}

双层循环

立方级别

N3

三个for嵌套的for循环

三层循环

显然,对于算法,它的时间复杂度越低越好。如果一个算法的时间复杂度过高,那么对于我们来说是不可接受了,因为它实在是太慢了。

通常O(1)<O(logN)<O(N)<O(NLogN)<O(N^2)<O(N^3)<O(2^N)

参考:算法第四版。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值