数据结构与算法--复杂度

复杂度

​ 复杂度也叫渐进复杂度,包括时间复杂度空间复杂度,用来分析算法执行效率与数据规模之间的增长关系

​ 复杂度分析可以在初期帮助程序员预估该程序的性能耗费

​ 时间复杂度用于表示算法的时间耗费与数据规模增长之间的关系

​ 空间复杂度用于表示算法的存储空间与数据规模增长之间的关系

时间复杂度

规则

  1. 总复杂度等于量级最大的那段代码的复杂度,比如说一个程序中存在两段不同时间复杂度的代码块:

    int f(int n) {
      	int sum = 0;
        
        for (int i = 0; i < n; ++i) {
        	sum ++;
        }
      
      	for (int i = 0; i < n; ++i) {
        	for (int j = 0; j < n; ++j) {
                sum ++;
            }
     	} 
        
        return sum;
     }
    

    第一个for循环的时间复杂度T1,和第二个嵌套for循环的时间复杂度T2分别为:
    T 1 ( n ) = O ( n ) , T 2 ( n ) = O ( n 2 ) T1(n)=O(n),T2(n)=O(n^2) T1(n)=O(n)T2(n)=O(n2)
    那么整个程序的时间复杂度为:
    T ( n ) = O ( m a x ( n , n 2 ) ) = O ( n 2 ) T(n)=O(max(n, n^2)) = O(n^2) T(n)=O(max(n,n2))=O(n2)

  2. 如果量级大的代码块有多个,而我们无法事先评估谁的量级大,就不能简单地省略其中一个,例如:

     int f(int n, int m) {
      	int sum = 0;
      
      	for (int i = 0; i < n; ++i) {
       		sum ++;
     	} 
     	
     	for (int i = 0; i < m; ++i) {
     		sum ++;
     	}
         
    	return sum;
     }
    

    该函数的时间复杂度为:
    O ( T 1 ( m ) + T 2 ( n ) ) = O ( m + n ) O(T1(m) + T2(n)) = O(m + n) O(T1(m)+T2(n))=O(m+n)

  3. 乘法法则: 嵌套代码的复杂度等于嵌套内外代码复杂度的乘积,例如:

     int f(int n, int m) {
      	int sum = 0;
      
      	for (int i = 0; i < n; ++i) {
        	for (int j = 0; j < m; ++j) {
                sum += j;
            }
     	} 
         
    	return sum;
     }
    

    该函数的时间复杂度为:
    T ( n ) = T 1 ( n ) ∗ T 2 ( m ) = O ( n m ) T(n) = T1(n) * T2(m) = O(nm) T(n)=T1(n)T2(m)=O(nm)

常见的时间复杂度

常 量 、 线 性 复 杂 度 : O ( 1 ) 、 O ( n ) 常量、线性复杂度:O(1)、O(n) 线O(1)O(n)

对 数 阶 、 线 性 对 数 阶 复 杂 度 : O ( l o g ( n ) ) 、 O ( n l o g ( n ) ) 对数阶、线性对数阶复杂度:O(log(n))、O(nlog(n)) 线O(log(n))O(nlog(n))

平 方 、 立 方 … k 次 方 阶 复 杂 度 : O ( n 2 ) 、 O ( n 3 ) … O ( n k ) 平方、立方…k次方阶复杂度:O(n^2)、O(n^3)…O(n^k) kO(n2)O(n3)O(nk)

指 数 阶 、 阶 乘 阶 复 杂 度 : O ( 2 n ) 、 O ( n ! ) 指数阶、阶乘阶复杂度:O(2^n)、O(n!) O(2n)O(n!)

​ 对于常量复杂度而言,执行时间不随 n 的增大而增长的代码,我们都记作 O(1) 。 一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)

​ 对于对数阶复杂度而言, 不管是以 2 为底、以 3 为底,还是以 10 为底,我们可以把所有对数阶的时间复杂度都记为O(logn)

​ 对于指数阶、阶乘阶复杂度而言, 当数据规模 n 增大,算法的执行时间会急剧增加 ,因此这两类时间复杂度的算法是非常低效的算法不推荐使用

最好、最坏、平均时间复杂度

​ 上述情况是程序必须执行完所有代码得出的时间复杂度。而在很多情况下,我们不需要进行完整的遍历或递归,例如查找一个数组中是否存在5:

int val[n];

bool find() {
	for (int i = 0; i < n; i++) {
		if (val[i] == 5) {
			return true;
		}
	}
	
	return false;
}

​ 最好情况时间复杂度是最理想情况下执行这段代码的时间复杂度,体现在上述代码中,就是数组第一个元素是5,因此最好情况时间复杂度为O(1)

​ 最坏情况时间复杂度是最糟糕情况下执行这段代码的时间复杂度 ,体现在上述代码中,就是数组中不存在元素5,因此最坏情况时间复杂度为O(n)

​ 平均情况时间复杂度是考虑所有可能发生的情况、概率、以及对应耗费的时间,然后取平均值,体现在上述代码中,就是考虑n+1种情况(5出现在0~n-1位置 和 5没有出现)及其概率、对应耗费的时间,得到:
1 ∗ 1 2 ∗ n + 2 ∗ 1 2 ∗ n + . . . + n ∗ 1 2 ∗ n + n ∗ 1 2 = 3 ∗ n + 1 4 1* \frac{1}{2*n} + 2 *\frac{1}{2*n} + ...+n*\frac{1}{2*n} + n*\frac{1}{2} = \frac{3*n+1}{4} 12n1+22n1+...+n2n1+n21=43n+1
​ 由于时间复杂度可以省略掉系数、低阶、常量,把这个公式简化之后得到的平均时间复杂度就是 O(n)

空间复杂度

规则

​ 规则与时间复杂度的三点类似,不再赘述

​ 我们可以通过空间复杂度,来预估程序所耗费的内存。比如:

const int N = 1000000;

int[] val = new int[N];

​ int型占四字节,对于O(n)空间复杂度的程序来说,当n为1e6时,耗费的内存为:
4 ∗ 1000000 / 1024 / 1024 ≈ 4 M B 4*1000000 / 1024 / 1024 ≈ 4MB 41000000/1024/10244MB

常见的空间复杂度

​ 比较常见的空间复杂度有:
O ( 1 ) 、 O ( n ) 、 O ( n 2 ) O(1)、O(n)、O(n^2) O(1)O(n)O(n2)
​ 相比时间复杂度, O(logn)、O(nlogn)等对数阶复杂度平时几乎用不到~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值