算法时间复杂度&空间复杂度(附github)

(*useful)标记:目前觉得有用的函数
//FIXME 标记:待补充

基本初等函数:

  • 幂函数:

一般地,形如y=xα(α为有理数)的函数,即以底数为自变量,幂为因变量,指数为常数的函数称为幂函数。例如函数y=x0 、y=x1、y=x2、y=x-1(注:y=x-1=1/x y=x0时x≠0)等都是幂函数。一般形式如下:


( α为常数,且可以是自然数、有理数,也可以是任意实数或复数。)
有理数: (正整数、0、负整数)和(正分数,负分数)的统称

  • 指数函数:

指数函数是数学中重要的函数。应用到值e上的这个函数写为exp(x)。还可以等价的写为ex,这里的e是数学常数,就是自然对数的底数,近似等于 2.718281828,还称为欧拉数。一般形式如下:


指数函数

(a>0, a≠1),所以指数函数处于一二象限

  • 对数函数:

一般地,函数y=logax(a>0,且a≠1)叫做对数函数,也就是说以幂(真数)为自变量,指数为因变量,底数为常量的函数,叫对数函数。

其中x是自变量,函数的定义域是(0,+∞),即x>0。它实际上就是指数函数的反函数,可表示为x=ay。因此指数函 数里对于a的规定,同样适用于对数函数。一般形式如下:

(a>0, a≠1, x>0,特别当α=e时,记为y=ln x)

时间复杂度

(一)时间频度

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

(二)时间复杂度

在刚才提到的时间频度中,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))为算法的渐进时间复杂度 ,简称时间复杂度。

符号O :Landau符号的作用在于用简单的函数来描述复杂函数行为,给出一个上或下(确)界:
某个算法的时间复杂度是 O(1), 常数阶, 优秀
某个算法的时间复杂度是 O(log2 n),对数阶, 良好
某个算法的时间复杂度是 O(n), 线性.次线性阶,还好
某个算法的时间复杂度是 O(n log2 n),线性对数阶, 还不错
某个算法的时间复杂度是 O(n2), 平方阶, 差一些
(*useful)

(三)求解算法的时间复杂度的具体步骤是

(1) 找出算法中的基本语句;
  算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
(2) 计算基本语句的执行次数的数量级;
  只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。
(3)用大Ο记号表示算法的时间性能。
  将基本语句执行次数的数量级放入大Ο记号中。

如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加。例如:

  for (i=1; i<=n; i++) {
        x++;  
  }
  for (i=1; i<=n; i++)  {
      for (j=1; j<=n; j++)  {
            x++;  
      }
}

第一个for循环的时间复杂度为Ο(n),第二个for循环的时间复杂度为Ο(n2),则整个算法的时间复杂度为Ο(n+n2)=Ο(n2)。

(*useful)
Ο(1): 表示基本语句的执行次数是一个常数,一般来说,只要算法中不存在循环语句,其时间复杂度就是Ο(1)。
多项式时间: O(log2n)、Ο(n)、 Ο(n log2n)、Ο(n2)和Ο(n3) 。 (推荐)
指数时间: Ο(2n)和Ο(n!) 。

(四)下面分别对几个常见的时间复杂度进行示例说明

(1)、O(1)

        Temp=i; i=j; j=temp;        

以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。注意:如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。

(2)、O(n2)

2.1. 交换i和j的内容

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

解:因为Θ(2n2+n+1)=n2(Θ即:去低阶项,去掉常数项,去掉高阶项的常参得到),所以T(n)= =O(n2);

2.2.

 for (i=1;i

解:
语句1的频度是n-1
语句2的频度是(n-1)(2n+1) = 2n2-n-1
f(n)=2
n2-n-1+(n-1) = 2n2-2;

Θ(2n2-2)=n2
该程序的时间复杂度T(n)=O(n2).

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

(3)、O(n)

  a=0;  
    b=1;                      ①  
      for (i=1;i<=n;i++) ②  
      {    
        s=a+b;    ③  
        b=a;     ④    
        a=s;     ⑤  
     }  

解: 语句1的频度:2,
语句2的频度: n,
语句3的频度: n-1,
语句4的频度:n-1,
语句5的频度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n).

(4)、O(log2n)

  i=1;     ①  
  while (i<=n)  
      i=i*2; ②  

解: 语句1的频度是1,
设语句2的频度是f(n), 则:2^f(n)<=n;f(n)<=log2n
取最大值f(n)=log2n,
T(n)=O(log2n)

(5)、O(n3)

 for(i=0;i

解:当i=m, j=k的时候,内层循环的次数为k当i=m时, j 可以取 0,1,...,m-1 , 所以这里最内循环共进行了0+1+...+m-1=(m-1)m/2次所以,i从0取到n, 则循环共进行了:0+(1-1)1/2+...+(n-1)n/2=n(n+1)(n-1)/6
所以时间复杂度为O(n3).

(五)常用的算法的时间复杂度和空间复杂度

这里讨论的是内部排序

swift写的部分算法实现链接

算法时间复杂度分析是一个很重要的问题,任何一个程序员都应该熟练掌握其概念和基本方法,而且要善于从数学层面上探寻其本质,才能准确理解其内涵。

空间复杂度

类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度

空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。
一个算法在计算机存储器上所占用的存储空间,包括:

  1. 存储算法本身所占用的存储空间
    • 与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。
  2. 算法的输入输出数据所占用的存储空间
    • 由要解决的问题决定,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。
  3. 算法在运行过程中临时占用的存储空间
    • 随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地"进行的,跟语言C,swift,js:对象开辟内存函数有关
    • 有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元

  • 当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);
  • 当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为0(log2n);
  • 当一个算法的空I司复杂度与n成线性比例关系时,可表示为0(n);
  • 若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;
  • 若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。

swift写的部分算法playground->github

你可能感兴趣的:(算法时间复杂度&空间复杂度(附github))