时间复杂度T(n)和空间复杂度S(n)

107 阅读1分钟

时间复杂度分析

忽略常量、低阶、系数

1. 只关注循环执行次数最多的一段代码

function cal(n) { 
   let sum = 0;
   let i = 1;
   for (; i <= n; ++i) {   //循环n次
     sum = sum + i;
   }
   return sum;
 }

T(n) = O(n)

2. 加法法则:总复杂度等于量级最大的那段代码的复杂度

function cal(n) {
   let sum_1 = 0;
   let p = 1;
   for (; p < 100; ++p) {   //  常数次数 T1(n) = O(1)
     sum_1 = sum_1 + p;
   }

   let sum_2 = 0;
   let q = 1;
  
   for (; q < n; ++q) {       //   n次 T2(n) = O(n)
     sum_2 = sum_2 + q;
   }
 
   let sum_3 = 0;
   let i = 1;
   let j = 1;
   for (; i <= n; ++i) {     //   n次 
     j = 1; 
     for (; j <= n; ++j) {    //   n次 T3(n) = O(n^2)
       sum_3 = sum_3 +  i * j;
     }
   }
 
   return sum_1 + sum_2 + sum_3;
 }

最终T(n) = max(T1,T2,T3) = O(n^2)

3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

嵌套代码求乘积:比如递归、多重循环等

function cal(n) {
   let ret = 0; 
   let i = 1;
   for (; i < n; ++i) {
     ret = ret + f(i); // 重点为  f(i)
   } 
 } 
 
function f(n) {
  let sum = 0;
  let i = 1;
  for (; i < n; ++i) {
    sum = sum + i;
  } 
  return sum;
 }

方法 cal 循环里面调用 f 方法,而 f 方法里面也有循环。

所以,整个 cal() 函数的时间复杂度就是,T(n) = T1(n) * T2(n) = O(n*n) = O(n2)

4. 多个规模求加法:比如方法有两个参数控制两个循环的次数,那么这时就取二者复杂度相加

function cal(m, n) {
  let sum_1 = 0;
  let i = 1;
  for (; i < m; ++i) {
    sum_1 = sum_1 + i;
  }

  let sum_2 = 0;
  let j = 1;
  for (; j < n; ++j) {
    sum_2 = sum_2 + j;
  }

  return sum_1 + sum_2;
}

以上代码也是求和 ,求 sum_1 的数据规模为 m、求 sum_2 的数据规模为 n,所以时间复杂度为 O(m+n)。

5. 多个规模求乘法:比如方法有两个参数控制两个循环的次数,那么这时就取二者复杂度相乘

function cal(m, n) {
  let sum_3 = 0;
   let i = 1;
   let j = 1;
   for (; i <= m; ++i) {
     j = 1; 
     for (; j <= n; ++j) {
       sum_3 = sum_3 +  i * j;
     }
   }
}

以上代码也是求和,两层 for 循环 ,求 sum_3 的数据规模为 m 和 n,所以时间复杂度为 O(m*n)。

公式:T1(m) * T2(n) = O(f(m) * g(n))

6. 多项式阶:随着数据规模的增长,算法的执行时间和空间占用,按照多项式的比例增长

对数阶:

let i=1;
while (i <= n)  {
   i = i * 2;
}

2^x = n => x=log(2)(n)=logn //常量、低阶、系数忽略
T(n) = O(logn)

7. 非多项式阶:随着数据规模的增长,算法的执行时间和空间占用暴增,这类算法性能极差。

指数阶:斐波那契

long aFunc(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return aFunc(n - 1) + aFunc(n - 2);
    }
}

显然运行次数,T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。 显然 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列,通过归纳证明法可以证明,当 n >= 1 时 T(n) < (5/3)^n,同时当 n > 4 时 T(n) >= (3/2)^n。 所以该方法的时间复杂度可以表示为 O((5/3)^n),简化后为T(n) = O(2^n)。

==常用的时间复杂度所耗费的时间从小到大依次是:==

==O(1) < O(logn) < (n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)==

空间复杂度

忽略常量、低阶、系数

算法的空间复杂度通过计算算法所需的存储空间实现(通俗讲就是定义了多少变量、数组长度等等);

function print(n) {
 const newArr = [];  //1个空间
 newArr.length = n; // n个空间
  for (let i = 0; i <n; ++i) {
    newArr[i] = i * i;
  }

  for (let j = n-1; j >= 0; --j) {
    console.log(newArr[i])
  }
}

S(n) = O(n)

==我们常见的空间复杂度就是 O(1)、O(n)、O(n2),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到==