# 时间复杂度
常见的 7 种时间复杂度
O(1)
常数复杂度O(log n)
对数复杂度O(n)
线性时间复杂度O(n^2)
平方O(n^3)
立方O(2^n)
指数O(n!)
阶乘
最简单判断时间复杂度的方法:查看当前函数执行的次数
我们不需要考虑常数系数,即
O(2n) === O(n)
所以,我们在写程序的时候,需要严格的考虑时间空间复杂度。
# 计算 1 + 2 + 3 + 4 + ... + n
从 1 到 n 的循环累加
let res = 0 for(let i = 1; i <= n; i++ ) { res += i }
因为进行了 n 次循环,所以时间复杂度是
O(n)
。求和公式
sum = n * (n + 1) / 2
程序只执行了一次,所以时间复杂度是
O(1)
。
# 面试四要素
- 首先和面试官确认题目的意思
- 想尽所有可能解决的方案
- 比较各个方法之间的时间和空间复杂度,找出最优的解决方案
- 测试结果
# 递归
核心:理解递归直线了多少次。
方法:利用递归的执行顺序,画出递归的树形结构,称之为递归状态树。
# 求斐波那契数列
递推公式:
F(n) = F(n - 1) + F(n - 2)
分析最简单的解决方案:
function fib(n: number): number {
if(n < 2) return n
return fib(n - 1) + fib(n - 2)
}
我们可以看到两个现象:
- 每多展开一层,节点数比上一层多一倍,所以大概判断在第 n 层的节点数为 2 ^ n 次方个,呈指数型增长;
- 各层之间的节点存在重复出现的节点 ,所以有非常多的冗余数据
# 主定理 (opens new window)
任何递归、分治的方法都可以使用主定理来计算时间复杂度
主要有下面四种情况:
翻译成中文:
算法 | 时间复杂度 | 递推公式 | 解释 |
---|---|---|---|
二分查找 | O(logn) | T(n) = T(n / 2) + O(1) | 每次都一分为二,越分越小 |
二叉树遍历 | O(n) | T(n) = 2 * T(n / 2) + O(1) | 每次都一分为二,但是每一边都是相等的时间复杂度 |
排序二维矩阵二分查找 | O(n) | T(n) = 2 * T(n / 2) + O(logn) | 一维二分查找的平方 |
归并排序 | O(nlogn) | T(n) = 2 * T(n / 2) + O(n) |
# 面试题
二叉树的前序、中序、后序遍历的时间复杂度
O(n)
:二叉树的每个节点有且仅访问一次图的遍历时间复杂度
O(n)
:图中的每个节点有且仅访问一次搜索算法:DFS(深度优先)、BFS(广度优先) 时间复杂度是多少?
都是
O(n)
,因为每个节点都只复杂一次。二分查找的时间复杂度
O(logn)
# 空间复杂度
关键点:
- 数组的长度
- 递归的深度