算法的定义
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。
算法的评价
证明一个算法的好坏,就要看它的时间复杂度 和空间复杂度。
时间复杂度
时间复杂度是指执行这个算法所需要的计算工作量,其复杂度反映了程序执行时间随输入规模增长而增长的量级
大O计算法
算法的复杂度通常用大O符号表述,定义为 T(n) = O(f(n))
f(n) 可以有的值:复杂度由好到坏
1. 常数型 O(1)
1. 对数型 O(log n)
1. 线性型 O(n)
1. 线性对数型 O(n log n)
1. 多项式型 平方型 O(n^2)、立方型 O(n^3)、K 次方型 O(n^k)
1. 指数型 平方底指数型 O(2^n)、立方底指数型 O(3^n)、K 次底指数型 O(k^n)
1. 阶乘型 O(n!)
一般算法优化是将时间复杂度转化为空间复杂度
计算方法
时间复杂度只关注最高数量级,且与之系数也没有关系。
时间复杂度分析的基本策略是:从内向外分析,从最深层开始分析;如果遇到函数调用,就要深入函数进行分析。
- O(1): 算法执行所需要的临时空间,不随着变量大小而变化,空间复杂度为一个常量,常见的普通语句相加等。
javascript
function sum() {
const a = 1;
const b = 2;
return a + b;
}
- O(logn):代码执行次数为 x,n 为目标数,符合 2^x = n,所以 x = log2(logn),常见的为二分查找。
javascript
function fun(n) {
let i = 1;
while (i < n) {
i = i * 2;
}
}
- O(n):常见的为一个遍历循环处理数据。
javascript
function fun(n) {
let sum = 0;
for (let i = 0; i < n.length; i++) {
sum += n[i];
}
return sum;
}
- O(nlogn):在O(logn) 的基础上,再执行一个 n 次循环
javascript
function fun(n) {
for (let j = 0; j < n; j++) {
let i = 1;
while (i < n) {
i = i * 2;
}
}
}
- O(n^2):循环嵌套,比如我们常见的冒泡排序算法
javascript
function sort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
- O(2^n):斐波那契,使用递归的情况下,因为使用了两次递归。
javascript
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
他们的复杂程度从高到底排序为
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(n...) < O(2^n) < O(n!)
注意:在斐波那契方法中,使用递归解法,会直接超时,因为 n 越大,计算量巨大,优化方法是用动态规划,时间复杂度为 O(n)、或者使用矩阵快速幂,时间复杂度为 O(log n)
思考:如何证明上面的算法是O(2^n)
下例,时间复杂度为 O(n!),基本不能称作为算法,n 越大,就容易卡死,小心尝试
function fun(n) {
console.log(n);
for (let i = 0; i < n; i++) {
fun(n - 1);
}
}
空间复杂度
算法的空间复杂度指的是在运行过程中临时占用的存储空间大小的量度。
- 代码所占用的空间:与算法本身的书写长短有关。
- 输入数据所占用的空间:调用函数时传递而来,与算法本身无关
- 辅助变量所占用的空间
一个算法的空间复杂度只考虑在运行过程中为局部变量所分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间、在函数体中定义的局部变量分配的存储空间两个部分。
计算方法
空间复杂度常见的为以下三个例子
- 所需要的临时空间不随着某个变量 n 的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)
js
function sum() {
const a = 1;
const b = 2;
return a + b;
}
-
定义一个数组的空间,数组的长度随着 n 的规模不同,会不一样,这里空间复杂度为 O(n)
function fun(n) {
let arr = [];
for (let i = 0; i < n.length; i++) {
arr.push(n[i]);
}
return arr;
} -
下例,最终形成一个二维数组的空间,空间复杂度为 O(n^2)
function fun(n) {
const arr = [];
for (let i = 0; i < n; i += 1) {
arr.push([]);
for (let j = 0; j < n; j += 1) {
arr[i].push(j);
}
}
}
小结
- 时间复杂度和空间复杂度仅仅代表了一种趋
- 好的算法往往更注重时间复杂度,空间复杂度在一个合理的范围即可