二叉树算法都是基于递归框架,我们要先搞清楚root节点他自己要做什么,然后根据题目要求选择前序,中序,后续的递归框架。
二叉树的难点在于如何通过题目的的要求思考出每个节点需要做什么。
写递归算法的关键是要明确函数的定义是什么,然后相信这个定义,利用这个定义推到最终结果,不要跳进递归细节。
root该做什么,什么时候做
/* 二叉树遍历框架 */
void traverse(TreeNode root) {
// 前序遍历
traverse(root.left)
// 中序遍历
traverse(root.right)
// 后序遍历
}
多叉树的遍历
function traverse (root: TreeNode) {
for (const child of root.children) {
// 前序遍历
traverse(child)
// 后序遍历
}
}
前序遍历和后序遍历是两个时间点
前序遍历的代码是在进入某个node之前的那个时间点执行,
后续遍历的代码是在离开某个node之后的那个时间点执行
这里联想一下回溯:
回溯的伪代码:
for 选择 of 选择列表 {
# 做选择
将该选择从列表中移除
路径.add(选择)
backtrack(路径, 选择列表)
# 撤销选择
路径.remove(选择)
将该选择再加入选择列表
}