算法的时间和空间复杂度

算法定义

   算法由控制结构(顺序、分支和循环3种)和原操作(指固有数据类型的操作)构成的,则算法时间取决于两者的综合效果。为了便于比较同一个问题的不同算法,通常的做法是,从算法中选取一种对于所研究的问题(或算法类型)来说是基本操作的原操作,以该基本操作的重复执行的次数作为算法的时间量度。不同的算法可能用不同的时间、空间或效率来完成同样的任务,一个算法的优劣可以用空间复杂度与时间复杂度来衡量.

时间复杂度

1、时间频度:一个算法执行所耗费的时间,在理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。

2、时间复杂度:在时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。此外,T (n) = Ο(f (n)) 表示存在一个常数C,使得在当n趋于正无穷时总有 T (n) ≤ C * f(n)。简单来说,就是T(n)在n趋于正无穷时最大也就跟f(n)差不多大。也就是说当n趋于正无穷时T (n)的上界是C * f(n)。其虽然对f(n)没有规定,但是一般都是取尽可能简单的函数。例如,O(2n2+n +1) = O (3n2+n+3) = O (7n2 + n) = O ( n2 ) ,一般都只用O(n2)表示就可以了。注意到大O符号里隐藏着一个常数C,所以f(n)里一般不加系数。如果把T(n)当做一棵树,那么O(f(n))所表达的就是树干,只关心其中的主干,其他的细枝末节全都抛弃不管。

常见的数量级大小:O(1)<O(logn)<O(n)<O(nlogn)<O(n2n2)<O(n3n3)<O(2n2n)<O(n!)

数量级 能承受的大致规模 常见算法
O(1) 任意 直接输出结果
O(logn) 任意 二分查找、快速幂
O(n) 以百万计(五六百万) 贪心算法、扫描和遍历
O(nlogn) 以十万计(三四十万) 带有分治思想的算法,如二分法
O(n2) 以千计数(两千) 枚举、动态规划
O(n3) 不到两百 动态规划
O(2n) 24 搜索
O(n!) 10 产生全排列
O(nn) 8 暴力法破解密码

O(1)叫常数时间;O(n)、O(n2)、O(n3)、O(n4)……叫做多项式时间;O(2n)、O(3n)……叫做指数时间。

3、算法的时间复杂度的具体步骤:

  ⑴ 找出算法中的基本语句;

  算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。

  ⑵ 计算基本语句的执行次数的数量级;

  只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。

  ⑶ 用大Ο记号表示算法的时间性能。

  将基本语句执行次数的数量级放入大Ο记号中。

  如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加。举个例子:第一个for循环的时间复杂度为Ο(n),第二个for循环的时间复杂度为Ο(n2),则整个算法的时间复杂度为Ο(n+n2)=Ο(n2)。

let x =1;
let y = 1;
for (let i=1; i<=n; i++)  {
     x++; 
} 
for (let j=1; j<=m; i++){
     for (let k=1; k<=m; j++)  {
        y++; 
      }    
}  
             

4、常见的时间复杂度进行示例

(1)O(1)

  let x=1;

  (2) O(n)

sum=0;                      (1次)  
for(i=1;i<=n;i++) {         (n+1次)  
     sum++;                 (n+1次)
} 

  

  Θ(2n+2)=n  (Θ即:去低阶项,去掉常数项,去掉高阶项的常参得到),所以

let sum=0;                 (1次)  
for(let i=1;i<=n;i++)      (n+1次)  
   for(let j=1;j<=n;j++)   ((n+1)(n+1)次)  
    sum++;                ((n+1)(n+1)次) 

  

  Θ(2n2+5n+3)=n2 T(n)=O(n2);

    一般情况下,对步进循环语句只需考虑循环体中语句的执行次数,忽略该语句中步长加1、终值判别、控制转移等成分,当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定的。

  (4)O(log2n)  

  

i = 1;
while(i<=n){
   i = i*2;
}

  

  

   i = 2, 4, 8, 16 ,32,64,128  => i = 2x, 假设x次(时间频度)后跳出循环,因为 i<=n ,所以2x <= n => x = log2n

    所以 T(n) = O( log2n)

while(n){
   n = n/2;
}

  

  128,64,32,16,8,4,2,...

   => 2x  = n => x = log2n

   (5)O(n3) 

   

for(i=0;i<n;i++)  
   {    
      for(j=0;j<i;j++)    
      {  
         for(k=0;k<j;k++)  
            x=x+2;    
      }  
   }  

  O(n3)

 

猜你喜欢

转载自www.cnblogs.com/xiaosongJiang/p/10003399.html