前言:
之前在公司中,培训的时候,经常听到一个词叫时间复杂度、空间复杂度,每次别人都是插一嘴的,简单介绍下,我都是云里雾里的听,培训完就忘得一干二净了,今天终于花时间给它整明白了!!!
基本概念:
语句频段:基本操作或语句执行的次数总和,称之为语句频段,记作f(n)
时间复杂度:算法或程序中基本操作或语句执行的次数总和,称之为时间复杂度,记作T(n),
且T(n) = O(f(n))
空间复杂度:表示算法的存储空间和数据规模之间的关系,度是对一个算法在运行过程中临时占用存储空间大小的一个量度。
空间复杂度的算法和时间复杂度算法类似
目的:
了解时间复杂度和空间复杂度,是为了让我们知道如何让你写的代码运行效率更高,占用空间更小
实例讲解:
假设每行的执行耗时都是一样的,都是t
时间复杂度
常数阶复杂度 O(1)
语句频度: f(n) = t + 1000t + 1000t + t = 2002t
时间复杂度:T(n) = O(f(n)) = O(2002) = O(1)
像这种不随着n的增大而增大,就叫常数阶复杂度 O(1) 。 并不表示代码只有一段
线性阶复杂度 O(n)
语句频度: f(n) = t + nt+ nt+ t = (2n+2)t
时间复杂度:T(n) = O(f(n)) = O(2n+2) = O(n)
当 n 很大时,我们可以忽略常数项,只保留一个最大量级即可,所以上述的时间复杂度可以简单的标记为O(n)
平方阶复杂度 O(n^2)
语句频度: f(n) = t + nt+ n^2t + n^2t + t = (2n^2 + n + 2)t
时间复杂度:T(n) = O(f(n)) = O(2n2 + n + 2) = O(n^2)
对数阶复杂度O(logn)
上面函数total1和total2有一个相同点:变量 i 从 1 开始取值,每循环一次乘以 2,当大于 n 时,循环结束。
所以真正循环了x次。2x =n;,所以 x = log2n。
所以上面两个函数时间复杂度都是 O(log2n)。
其他复杂度O(m+n)和O(m*n)
由于我们无法估量m和n,谁的量级更大,所以不能忽略任何一个
复杂度累计原则:
语句频度: f(n) = t + nt+ n^2t + n^2t + t + 1000t + 1000t + t + nt + nt +t = (2n^2+ 3n+2004)t
时间复杂度:T(n) = O(f(n)) = O(2n2 + n + 2) = O(n^2)
常见的复杂度:
上面的时间复杂度可以分为两类:多项式量级(n不是作为底数的阶乘)和非多项式量级(n是作为底数的阶乘)。非多项式量级只有两个:O(2n) 和 O(n!)
当数据规模 n 增长时,非多项式量级的执行时间就会急剧增加,所以,非多项式量级的代码算法是非常低效的算法。越高阶复杂度的算法,执行效率也越低。
时间复杂度的算法总结如下:
1、将所有的时间复杂度进行累加
2、去掉时间复杂度中所有的常量
3、取最大的时间复杂度
空间复杂度
案例:
语句频度: f(n) = t + nt+ nt = (2n+1)t
空间复杂度:T(n) = O(f(n)) = O(2n+1) = O(n)
空间复杂度和时间复杂度算法一致,常见的空间复杂度:O(1)、O(n)、O(n^2)、O(n^3)
下面让我们来看下几个有意思的案例:如何进行时间复杂度优化
由上面的例子可以看出累加的时间复杂度是O(n),那如何进行优化呢,优化到时间复杂度是O(1)
高中的时候,我们学了一个等比数列,对于累加器可以换成等比数列进行求和
上面一段代码的时间复杂度是O(n),下面一段时间复杂度是O(1),这样我们时间复杂度就由原来的O(n)降为O(1)
还有一段经典的代码,时间冒泡排序由原来的O(n^2)变为最佳的是O(n)
语句频度: f(n) =2 + (n-1) + 5n(n-1)/2 = 5n2/2-3n/2+2
空间复杂度:T(n) = O(f(n)) = O(2n+1) = O(n)
加入了交换标志didSwap,i=0第一遍遍历,进行n-1次比较,因为数组本身正序不会进行任何一次交换,那么第一遍遍历完交换标志didSwap依然为false,算法直接返回结束。
只进行一次遍历,n-1次比较,算法就结束,显然冒泡排序最佳情况的时间复杂度是O(n)。