如何计算算法的时间复杂度和空间复杂度
时间复杂度计算: 时间复杂度衡量算法运行所需时间随输入规模变化的增长情况,通常用大O符号表示。
- 步骤:
- 分析基本操作:找出算法中最核心操作(如循环、递归等),并统计其执行次数。
- 忽略忽略常数项:只关注输入规模
n对执行次数的影响,忽略常数因子和低阶项。 - 找出增长趋势:确定算法中最耗时的部分,并用大O符号表示增长趋势。
- 常见复杂度级别:
- O(1):常数时间,算法运行时间不随输入规模变化。
- O(logn):对数时间,常见二分查找。
- O(n): 线性时间,常见单层循环。
- O(nlogn):常见归并排序、快速排序的平均情况
- O(n²):平方时间,常见嵌套循环
- O(2ⁿ):指数时间,常见递归问题(斐波那契数列的朴素递归)
- O(n!):阶乘时间,常见全排列问题的暴力解法。
function sumArray(arr) {
let sum = 0; // O(1)
for (let i=0; i < arr.length; i++) { // O(n)
sum += arr[i]; // O(1)
}
return sum; // O(1)
}
总时间复杂度:O(n)
空间复杂度的计算: 空间复杂度衡量算法运行时占用的额外内存空间(不包括输入数据本身)。
- 步骤:
- 分析变量存储:统计算法中定义的变量、数组、递归栈等占用的空间
- 忽略常数项:只关注输入规模n对空间的影响
- 找出增长趋势:确定随着输入规模增长,额外空间需求的变化趋势
- 常见复杂度级别:
- O(1):常数空间,算法不依赖输入规模
- O(n):线性空间,常见需要额外存储输入的算法(动态规划表)
- O(n²):平方空间,常见二维矩阵操作
function createArray(n) {
let arr = new Array(n); // O(n)
return arr;
}
空间复杂度:O(n)
什么是栈?JavaScript中有哪些栈的具体应用?
- 栈(Stack)是一种 先进后出(LIFO,Last In First Out) 的数据结构
- 栈的基本操作:
- push:将元素压入栈顶
- pop:从栈顶弹出元素
- peek/top:查看栈顶元素
- isEmpty:判断是否为空
- 特点:
- 只能在一端(栈顶)进行插入和删除操作
- 常用于递归调用、括号匹配、表达式求值等场景
JavaScript中栈的具体应用:
- 1. 函数调用栈(Call Stack):
- JavaScript的执行环境是单线程,函数调用会按照栈的形式管理
- 当一个函数被调用时,它会被压入调用栈;函数执行完毕后会从栈中弹出
function a() {
console.log('a');
b();
}
function b() {
console.log('b');
c();
}
function c() {
console.log('c');
}
a(); // 调用
调用栈变化:
1. 调用 a() -> [a]
2. 调用 b() -> [a, b]
3. 调用 c() -> [a, b, c]
4. c() 执行完弹出 -> [a, b]
5. b() 执行完弹出 -> [a]
6. a() 执行完弹出 -> []
- 2. 括号匹配问题:
- 栈常用于检查括号是否匹配
function isValid(s) {
const stack = [];
const map = { `)`: `(`, `}`: `{`, ']': `[` };
for (let char of s) {
if (char in map) {
if (stack.pop() !== map[char]) return false;
} else {
stack.push(char);
}
}
return stack.length === 0;
}
console.log(isValid("({[]})")); // true
console.log(isValid("({[})")); // false
- 3. 浏览器历史记录:
- 浏览器的前进、后退功能可用2个栈实现
- 一个栈保存前进页面,一个栈保存后退页面
- 4. 递归
- JavaScript的递归调用本质上是依赖调用栈
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
每次递归调用会将当前函数压入调用栈,直到递归结束再逐个弹出。
- 5. 表达书求值:
- 栈可用中缀表达式转为后缀表达式(逆波兰表达式)以及后缀表达式的求值。
- 6. 深度优先搜索(DFS):
- 使用栈模拟递归过程,遍历图或树结构。