- 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
目录
- 时间复杂度
- 几种常见的时间复杂度
- 空间复杂度
- 常见
数据结构与算法的时间、空间复杂度总结 - 参考
- 总结
一 时间复杂度
在前一文【算法】 时间复杂度,空间复杂度 中我们说了时间复杂度,而没有说 空间复杂度,本文重点说一下 空间复杂度
衡量代码好坏有两个非常重要的标准就是:
运行时间和占用空间,就是我们后面要说到的时间复杂度和空间复杂度,也是学好算法的重要基石
通常一个算法由
控制结构(顺序,分支,循环三种)和原操作(固有数据类型的操作)构成,而算法时间取决于两者的综合效率。
function fn(){
var a = 1;
var b = 2;
return null
}
二 几种常见的时间复杂度
1) 常数阶O(1)
O(1) 只是常量级时间复杂度表示法,并不是代码只有一行,比如说下面这段代码
function total() {
var sum = 0;
for(var i=0;i<100;i++) {
sum += i;
}
}
虽然有这么多行,即使 for 循环执行了 100 次,但是代码的执行时间不随 n 的增大而增长,所以这样的代码复杂度就为 O(1)。
2) 线性阶O(n)
只有一层循环或者递归等,时间复杂度就是 O(n)
function fn1(n){
for( let i = 0; i < n; i++){
console.log(i)
}
}
function fn2(n){
while( --n > 0){
console.log(n)
}
}
3) 平方阶O(n²)
比如嵌套循环,如下面这样的,里层循环执行 n 次,外层循环也执行 n 次,总执行次数就是 n x n,时间复杂度就是 n 的平方,也就是
O(n²)。
function fn1(n){
for( let i = 0; i < n; i++){
for( let j = 0; j < n; j++){
console.log(i)
}
}
}
如果是多项式,取最高次项,所以这个时间复杂度也是
O(n²)
或者下面这样,以运行时间最长的,作为时间复杂度的依据,所以下面的时间复杂度就是 O(n²)
function fn2(n){
for( let k = 0; k < n; k++){
console.log(k)
}
for( let i = 0; i < n; i++){
for( let j = 0; j < n; j++){
console.log(j)
}
}
}
function fn3(n){
if( n > 100){
for( let k = 0; k < n; k++){
console.log(k)
}
}else{
for( let i = 0; i < n; i++){
for( let j = 0; j < n; j++){
console.log(j)
}
}
}
}
3.1) 立方阶O(n³) 、K次方阶O(n^k)
参考上面的O(n²) 去理解就好了,O(n³)相当于三层n循环,其它的类似。
4) 对数阶O(logN)
对数个底数相乘 = 真数 比如2 * 2 * 2 = 8 log2 8 3个2 相乘等于8
function fn1(n){
let i = 0
while(n > 1){
n = n/2
i++
}
return i
}
console.log( fn1(16) ) // 4
function fn2(n){
for(let i = 0; i < n; i *= 2){
console.log("")
}
}
fn2( 16 )
5) 线性对数阶O(nlogN)
线性对数阶O(nlogN) 其实非常容易理解,将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)。
就拿上面的代码加一点修改来举例:
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
6) O(m+n)、O(m*n)
再来看一段特殊的代码时间复杂度,比如说
function total(m,n) {
var sum1 = 0;
for (var i = 0; i < n; i++) {
sum1 += i;
}
var sum2 = 0;
for (var i = 0; i < m; i++) {
sum2 += i;
}
return sum1 + sum2;
}
因为我们无法评估 m 和 n 谁的量级比较大,所以就不能忽略掉其中一个,这个函数的复杂度是有两个数据的量级来决定的,所以此函数的时间复杂度为 O(m+n);那么 O(m*n) 的时间复杂度类似。
三 空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,所谓的临时占用存储空间指的就是代码中**「辅助变量所占用的空间」,它包括为参数表中「形参变量」分配的存储空间和为在函数体中定义的「局部变量」**分配的存储空间两个部分。我们用 S(n)=O(f(n))来定义,其中n为问题的规模(或大小)。
通常来说,只要算法不涉及到
动态分配的空间,以及递归、栈所需的空间,空间复杂度通常为O(1)。一个一维数组a[n],空间复杂度O(n),二维数组为O(n^2)
空间复杂度就是算法需要多少内存,占用了多少空间; 常用的空间复杂度有
O(1)、O(n)、O(n²)
1) O(1)
只要不会因为算法里的执行,导致额外的空间增长,就算是几万行,空间复杂度也是
O(1),时间复杂度也是 O(1)时,空间复杂度往往也是 O(1)
2) O(n)
n 的数值越大,算法需要分配的空间就需要越多,来存储数组里的值,所以它的空间复杂度就是
O(n),时间复杂度也是 O(n)
function fn(n){
let arr = []
for( let i = 1; i < n; i++ ) {
arr[i] = i
}
}
3) O(n²)
O(n²) 这种空间复杂度一般出现在比如二维数组,或是矩阵的情况,就是遍历生成类似这样格式的
let arr = [[5],[2],[0]]
四 常见数据结构与算法的时间、空间复杂度总结
1) 数据结构
2) 堆
3) 图
4) 排序算法
5) 搜索算法
参考
总结
-
通常来说,只要算法不涉及到
动态分配的空间,以及递归、栈所需的空间,空间复杂度通常为O(1)。一个一维数组a[n],空间复杂度O(n),二维数组为O(n^2) -
空间复杂度就是算法需要多少内存,占用了多少空间; 常用的空间复杂度有
O(1)、O(n)、O(n²);这三种空间复杂度和时间复杂度一一对应