时间复杂度分析
忽略常量、低阶、系数
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) 这样的对数阶复杂度平时都用不到==