【前言】
我们程序员写的代码,代码的质量除了阅读性要好,可扩展性外,更重要的就是耗时要尽量少,占用的内存尽量少。
那么在代码里,耗时多少则用时间复杂度表示,占用的内存多少则可以用空间复杂度表示。
本文只做简单介绍,让不明白的同学明白这两个概念,并不深入。
【时间复杂度】
(1)什么是时间复杂度?
书面的语言不用说太多,说太多反而脑袋晕,最好的解释就是举例说明:
例如:小明和小花同时阅读一篇n页的书,小明每天读1页,小花每天读剩下书的一半(以页为单位,剩余1页时直接读完),那么他们多少天可以读完?假设我们以T(n)来表示之间的关系。
这里我们称n为规模,T(n)为语句频度。
从这里可以看出来,小明需要n天,则T(n) = n。小花需要T(n) = log2n + 1(其中2为底数,n为真数,下同)
那么这里的时间复杂度为小明为O(n),小花为O(log2n),如果不懂怎么来的,看下面时间复杂度的计算。
(2)程序的时间复杂度
简单的来说,一个循环为n的时间复杂度为n,双重循环都为n的时间复杂度为n的二次方,三重循环都未n就是n的三次方,以此类推。
【时间复杂度计算】
(1)T(n) = 1000
(2)T(n) = log2n
(3)T(n) = 2n
(4)T(n) = 3n^3
(5)T(n) = n^3 + 2n + 1
时间复杂度我们用O来表示,他表示一个函数在随着n无限大的时候,最影响时间的那个项,然后省去其常数系数,下同空间复杂度。
解:
(1)这个为常数阶,不涉及变量,因此他的时间复杂度为1,记为O(1)
(2)这个为对数阶,不管是log2n,还是100log2n,最影响他的就是log2n,所以他的时间复杂度为O(log2n)
(3)这个为线性阶,不管是2n,还是3n,还是3n+10000,最影响他的是n,所以他的时间复杂为O(n)
(4)这个为指数阶,不管是3n^3,还是(5)那样 n^3 + 2n + 1,最影响他们的是n^3,因此它的时间复杂度都为O(n^3)
【空间复杂度】
(1)什么是空间复杂度?
例子:已知一亩地一年可以收获水稻1千斤,那么要收获n千斤水稻需要多少时间,多少地?
下面是小明和小花的做法:
第一种:小明一次性种了n亩水稻,那么一年则可以达到目标,但这个方式需要耗费了大量的土地,如果是100个1千斤,那小明家得有多大的地盘才能种的完,且在播种和收获的时候不得累死。
第二种:小花一年种1亩水稻,那么n年则可以达到目标,这个方式虽然节约了不少土地,但却需要耗费大量的时间,如果是目标100个1千斤,恐怕小花到死也完成不了。
根据上面所学:我们不难看出,不管多少目标产量,
小明的时间始终为1年(其实不管是1年还是5年,只要是具体的一个数),则时间复杂度记为O(1);小花的时间则需要n年,时间复杂度记为O(n)。
由于程序的数据都是存在内存上的,都是一个单位一个单位的,空间复杂度就好比这里的土地,运行程序的环境内存就那么点大,只从耗内存方面来说,肯定是花的越少越好。
所以,从空间复杂度来说,小明的方式则需要n亩地,记为O(n);小花的方式则始终为1亩地(其实不管是1亩,还是5亩,只要是一个具体的数),则空间复杂度记为O(1);
由于时间英文为Time,空间英文为Space,那么他俩的两个复杂度可记为:
小明:T(n) = O(1), S(n)=O(n) 小花:T(n) = O(n), S(n)=O(1);
(2)程序的空间复杂度(这里n是一个变量)
因为a1的值不管是取0还是取10000,他所占的单位内存始终是1个,故空间复杂度为1。
a2的单位内存个数取决于变量n,所以不能确定到底占用多少个内存,故空间复杂度为n。
a3是一个n*n的二维数组,所以需要n的平方个内存单位,故空间复杂度为n^2。
【两复杂度关系】
从上面的水稻问题可知,我们在书写一段代码的时候,时间复杂度和空间复杂度往往是呈反相关的。
即时间尽可能的少的时候,往往牺牲的内存比较大,即程序员口中常说的 “拿内存换性能” 之说。
一段代码逻辑是否有真的存在最优解?即耗时少,消耗内存较低的写法。
答案是肯定的,大学里《数据结构与算法》就是研究的这个,所以只要算法学习好了,代码的质量就会蹭蹭蹭的获得提高。
【考考你】
好了,我想到这里,两个复杂度的意思你是否已经弄明白了吧,明白的点个赞,没明白的留个言。
明白的,不妨试试看看那下面这段代码的时间复杂度T(n)、空间复杂度S(n)分别是多少?回答后连续下滑查看答案。
for(int i=0; i<n; i++) {
int[] arr1 = new int [5];
for (int j = 0; j < n/2; j++) {
int[] arr2 = new int [n];
}
}
解:
我们首先来看时间语句频度T(n)和n之间的关系:
由于外层循环式n遍,内层循环式n/2遍,故而T(n) = n * (1/2) n = (1/2) n^2;
我们再看空间空间语句频度S(n)和n之间的关系:
由于第一层循环中,循环了n遍,每遍都要使用5个单位内存,因为n遍就是5n个单位内存。
我们再看内层循环,循环(n/2)遍,每遍需要使用(n/2) * n 个单位内存,由于外层还有n遍,
则内层一共需要n * (n/2) * n = (1/2) n^3单位内存,加上前面的5n,则S(n) = (1/2) n^3 + 5n。
综上: T(n) = (1/2) n^2;S(n) = (1/2) n^3 + 5n。
随着n趋于无穷大的时候,最影响他们的是(1/2) n^2和(1/2) n^3,我们省略其常数系数即可得到复杂度关系,
即这段代码的时间复杂度为T(n) = O( n^2),空间复杂度为S(n) = O(n^3)