时间复杂度和空间复杂度是什么?
一、简介
【时间复杂度】是一个算法运行所需要的时间。【空间复杂度】是一个算法运行所需要的储存空间。
它们常常被人们用来检测一个算法的质量好坏。在实际操作中,这两个指标往往无法同时兼顾。这需要设计者综合各方面的实际情况做出取舍。算法原本是数学领域中涉及到的知识。后来,随着计算机的不断升级和发展,也开始在计算机领域中的大量使用。
在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
时间频度
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度,记为 T(n)。因此,另一种更为通用的方法就出来了:「 大O符号表示法 」,即 T(n) = O(f(n))
最坏时间复杂度
二、时间复杂度量级
1、常数时间
若对于一个算法,T(n)的上界与输入大小无关,则称其具有常数时间,记作 O(1) 时间。一个例子是访问数组中的单个元素,因为访问它只需要一条指令。但是,找到无序数组中的最小元素则不是,因为这需要遍历所有元素来找出最小值。这是一项线性时间的操作,或称 O(n) 时间。
2、对数时间
3、线性时间
如果一个算法的时间复杂度为O(n),则称这个算法具有线性时间,或O(n)时间。非正式地说,这意味着对于足够大的输入,运行时间增加的大小与输入成线性关系。例如,一个计算列表所有元素的和的程序,需要的时间与列表的长度成正比。这个描述是稍微不准确的,因为运行时间可能显著偏离一个精确的比例,尤其是对于较小的n。
4、线性对数时间
若一个算法时间复杂度T(n) = O(nlog n),则称这个算法具有线性对数时间。简单理解就是将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 O(n) * O(logN),也就是了O(nlogN)。从其表达式我们也可以看到,线性对数时间增长得比线性时间要快
...
三、复杂度与时间效率的关系
其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n,那么这个算法时间效率比较高 ,如果是 2n,3n,n!,那么稍微大一些的n就会令这个算法不能动了,居于中间的几个则差强人意。
四、代码复杂度分析
1、常数阶O(1)
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
上述代码在执行的时候,它消耗的时间并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度。
2、线性阶O(n)
for(i=1; i<=n; ++i) { j = i; j++; }
for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度。
3、对数阶O(logN)
int i = 1; while(i<n) { i = i * 2; }
从上面代码可以看到,在while循环里面,每次都将 i 乘以 2,乘完之后,i 距离 n 就越来越近了。我们试着求解一下,假设循环x次之后,i 就大于等于 n 了,此时这个循环就退出了,也就是说 2 的 x 次方等于 n,那么 x = log2^n
也就是说当循环 log2^n 次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(logn)
4、线性对数阶O(nlogn)
int num1,num2; for(int i=0; i<n; i++){ num1 += 1; for(int j=1; j<=n; j*=2){ num2 += num1; } }
可以看出,决定算法复杂度的是执行次数最多的语句,这里是num2 += num1,一般也是最内循环的语句。分析步骤如下:
- 找到执行次数最多的语句
- 计算语句执行次数的数量级
- 用大O来表示结果
执行次数最多的语句为num2 += num1 >>> 语句频度可表示为 T(n) = n*log2n >>> 大O表示为:T(n) = O(n*log2n)