一、时间复杂度和空间复杂度分别表示什么?
T(n) = O(f(n)); //时间复杂度 S(n) = O(f(n)); //空间复杂度
它们都叫做大O表示法,
T代表算法执行总时长,
S代表算法占用总空间,
f(n)代表执行的总次数。
二、时间复杂度
1.时间复杂度的定义
首先,时间复杂度不代表代码执行的时间,算法的时间复杂度说的是一个算法的执行时间根据规模增长的一个趋势,而并不是代码执行的具体时间
2.几种常见的时间复杂度
| 类型 | 大O表示法 | 术语 | 说明 |
|---|---|---|---|
| 15 | O(1) | 常数阶 | 最低的时间复杂度,无论输入数据增大多少倍,耗时永远不变 |
| 3logn | O(logn) | 对数阶 | 以2为底数的log,数据增大n倍,耗时更加logn倍,比如数据量增大256倍时,耗时只增大8倍,因为它术语二分查找法,每次取一半来进行排除,所以256个数据就只需要查找八次就可以找到目标 |
| 3n+5 | O(n) | 线性阶 | 去除常量,只剩下n,数据量增大几倍,耗时也增大几倍 |
| 5n^2+3n+1 | O(n^2) | 平方阶 | n的平方,计算的复杂度随着数据量的平方根增长 |
| 6n^3+4n+2 | O(n^3) | 立方阶 | n的立方,计算的复杂度随着数据量的立方根增长 |
| 2^n+1 | O(2^n) | 指数阶 | 左边为底数,右上角为指数,结果为幂,随着数据量的指数增长 |
| n!+3 | O(n!) | 阶乘阶 | n! = n*(n-1),随着数据量的阶乘增长 |
3.常见的时间复杂度代码示例、
3.1 常数阶O(1) :只是常量级时间复杂度表示法,并不是代码只有一行
function fn() {
let sum = 0;
for(let i = 0;i < 100;i++) {
sum += i;
}
return sum;
}
虽然有这么多行,即使 for 循环执行了 100 次,但是代码的执行时间不随 n 的增大而增长,所以这样的代码复杂度就为 O(1)。
3.2 线性阶O(n)
代码如下:
for(let i = 0;i < n; i++){
let j = i;
j++;
}
这段代码在执行时,for循环里面的代码会执行n次,所以它消耗的时间会随着n的变化而变化,这样的代码都可以用O(n)来表示
3.3 对数阶O(logN)
代码如下:
let i = 1;
while(i < n) {
i = i * 2;
}
这段代码在执行时,while循环里面,每次都将 i * 2 ,乘完之后, i 会距离 n 越来越近,一直到 i 大于 n 为止。这样的代码都可以用O(logN)来表示
3.4 线性对数阶O(nlogN)
代码如下:
for(let k = 0; k < n; k++) {
let i = 1;
while(i < n) {
i = i * 2;
}
}
线性对数阶O(nlogN) 其实非常容易理解,将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)
3.5 平方阶O(n²)
代码如下:
for(let i = 0; i < n; i++) {
for(let i = 1; i < n; i++) {
let j = i;
j++;
}
}
平方阶O(n²) 就更容易理解了,如果把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了。
3.6 立方阶O(n³)、K次方阶O(n^k)
参考上面的O(n²) 去理解就好了,O(n³)相当于三层n循环,其它的类似
三、空间复杂度
1. 空间复杂度的定义
空间复杂度就是指的占用内存的趋势
2. 空间复杂度比较常用的有:
- O(1)
- O(n)
- O(n²)
2.1 空间复杂度 O(1)
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)
代码如下:
let i = 1;
let j = 2;
++i;
j++;
int m = i + j;
代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)
2.2 空间复杂度 O(n)
代码如下:
let m = new Array(n);
for(let i = 1; i < n; i++)
{
let j = i;
j++;
}
这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,这段代码的2-6行,虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)
2.3 空间复杂度 O(n²)
代码如下:
let arr=[];
for (let i = 0; i < n; i++) {
arr[i] = i;
for (let j = 0; j < n; j++) {
arr[i][j] = j;
}
}
只要不是在循环里边不停的声明变量,只改变值的话是不会层架空间复杂度的
四、总结
分析算法执行效率与数据规模之间的增长关系,可以粗略的表示,越高阶复杂度的算法,执行效率越低。
复杂度学习之后,有时候可以避免写出效率低的代码。