时间复杂度详解

一个程序的优劣主要取决于其运行时间,除去一些我们无法处理的因素(所使用的编译器,计算机的硬件配置)以外,剩下的最主要的因素则是所使用的算法以及对该算法的输入
那么一个算法的优劣我们应该如何评判呢?别着急,继续看下去。
本片博文讲从一下几个方面来向大家说明什么是时间复杂度以及我们为什么需要时间复杂度:

  • 数学基础
  • 模型&&要分析的问题
  • 运行时间的计算
  • 总结

数学基础

估计算法资源消耗所需的分析一般来说是个理论问题,因此我们需要一套正式的系统构架。
定义:如果存在常数c,n使得N>=n时,T(N)<=cf(N),则记为T(N)=O(F(N));
对这句话的解释是:总会存在某一个常数n,使得当N大于等于n以后,cf(N)总是至少大于等于T(N),从而以至于我们可以忽略常数因子。
这个定义的目的是在函数间建立一种相对的级别。给定两个函数,通常存在一些点,在这些点上一个函数的值小于另一个函数的值,因此像f(N)<g(N)这样的声明是没有什么意义的,于是呢,我们比较它们的相对增长率,当将相对增长率应用在算法分析的时候我们将会明白为什么它是重要的度量。
例子:T(N)=1000N,f(N)=NN,n=1000,c=1,我们也可以让n=10,c=100。因此我们可以说1000N=O(NN)(N平方级)。这种记法为大O记法
我们掌握的重要结论有:
1.如果T1(N)=O(f(N))且T2(N)=O(g(N)),那么
(a),T1(N)+T2(N)=max(O(f(N)),O(g(N)))
(b) ,T1(N)*T2(N)=O(f(N)*g(N))
2.如果T(N)是一个k次多项式,则T(N)=O(N**K)(N的k次方)
3.对于任意常数k,logN的k次方=O(N)。它告诉我们对数增长的非常缓慢。
下表列出一些常见的增长率:
在这里插入图片描述
注意:
1.不要将常数项或者低阶项放进大O。
2.不要说成f(N)<=O(g(N)),因为定义已经隐含有不等式了。

模型&&要分析的问题

模型:
为了在正式的框架中分析算法,我们需要一个计算模型。我们的模型基本上是一台标准的计算机,在机器中指令被顺序的执行。该模型有一个标准的简单指令系统,如加法,乘法,比较和赋值等等。但不同于实际计算机的情况是,模型机做任何一个简单的操作都恰好花费一个时间单元,为了合理起见,我们将假设我们的模型像一台现代计算机那样有固定范围的整数并且不存在诸如矩阵求逆或者排序运算等,它们显然不能在一个时间单位内完成,我们还假设模型机有无限的内存。
要分析的问题:
与文首呼应:要分析的最重要的资源一般来说就是运行时间,除去一些我们无法处理的因素(所使用的编译器,计算机的硬件配置)以外,剩下的最主要的因素则是所使用的算法以及对该算法的输入
一般来说,我们估算一个算法所需要花费的时间往往是最坏的情况而不是平均时间。原因有:
1.它对所有的输入提供了一个界限,包括特别糟糕的输入情况,而平均情况分析不提供这样的界。
2.平均情况的界计算起来通常更加的困难。

运行时间的计算

为了简化分析,我们将采用如下的约定:
1.不能存在特定的时间单位,因此我们抛弃一些常数项
2.大O运行时间,抛弃低阶项
注意:我们必须仔细,绝对不要低估程序的运行时间,实际上,分析的结果为程序在一定的时间范围内能够终止运行提供了保障,程序可能提前结束,但绝对不可能拖后,因此,在这里我们将会分析其最坏情况下所花费的时间。
一个简单的例子:

   1 int sum(int n)
   2 {
   3     int i, sum;
   4     sum = 0;
   5     for (i = 1; i <= N; i++) {
   6         sum += i*i*i;
   7     }
   8     return sum;
   9 }

第4行第8行各站一个时间单元
第6行占4N个时间单元(一个赋值,一个加法,两个乘法,共四个原子操作,循环N次)。
第5行占2N+2个时间单元(一个初始化,N+1次比较,N次自增)。
1+4N+2N+2=6N+4。因此我们对于该算法得到的时间函数的大O记法为O(N)。
但是紧接着我们就会发现,如果每次分析一个程序我们都要演示以上的所有步骤,那么,很快这项任务就变成了不可执行的工作,幸运的是,由于我们有了大O的结果,因此就存在许多可以采取的捷径并且不影响最后的结果。例如:第六行每次执行显然是O(1)语句,因此精确计算它究竟是2,3,4个时间单元的行为是不明智的;这无关紧要,与第一行的for循环相比显然是不重要的,所以在此花费时间也是不明智的行为,这使得我们的得到了若干一般法则:
法则一:

一次for循环的运行时间至多是该for循环内语句的运行时间乘以迭代的次数
法则二:

从里向外分析这些循环。在一组嵌套循环内部的一条语句总的运行时间为该语句的运行时间乘以该组所有的for循环的大小的乘积
例:

   1 for (int i = 0; i < N; i++) {
   2     for (int j = 0; j < N; j++) {
   3         k++;                                                                        
   4     }
   5 }

所以这个算法的时间复杂度为:O(N*N);
法则三——顺序语句

将各个语句的运行时间求和即可(这意味着其中的最大值就是所得的运行时间,见上文的法则1)

法则四——if/else语句

一个ifelse语句的运行时间从不超过判断加上其中一个分支中运行时间最长的一个分支的运行时间
注意:如果有函数调用,那么要首先分析函数调用。

总结

时间复杂度用来描述一个算法所花费的时间的增长趋势,它并不是一个确切的值,甚至存在极大的偏差,但是我们用它来描述一种趋势已经足以。

参考文献:《数据结构与算法——C语言描述》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值