携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情
二叉树由根节点、左子树、右子树构成。它有三种遍历方式——前序遍历,中序遍历和后序遍历,他们的遍历顺序分别为:
- 前序遍历:根节点=>左子树=>右子树
- 中序遍历:左子树=>根节点=>右子树
- 后序遍历:左子树=>右子树=>根节点
这些遍历都可以用两种算法来解决,那就是递归和迭代。
144. 二叉树的前序遍历
- 根据前序遍历的定义,每一次递归,会先打印根节点,再遍历左子树,再遍历右子树
var preorderTraversal = function(root) {
let arr = []
var getFun = function (root) {
if (!root) return
arr.push(root.val)
root.left && getFun(root.left)
root.right && getFun(root.right)
}
getFun(root)
return arr
};
- 对于迭代法,需要用到栈的数据结构,因其后进先出的特性,打印当前根节点后,需要先往栈中压入右节点,然后再遍历左节点
var preorderTraversal = function(root) {
let res = []
let stack = [root]
while(stack.length) {
let node = stack.pop()
while (node) {
res.push(node.val)
stack.push(node.right)
node = node.left
}
node && node.right && stack.push(node.right)
}
return res
};
这两种方案的时间复杂度都是O(n),n为节点个数,因为每个节点都会被遍历一次,空间复杂度为O(n),分别为递归过程中和迭代过程中栈的开销
94. 二叉树的中序遍历
- 根据中序遍历的定义,每一次递归,会先遍历左子树,再打印根节点,再遍历右子树
代码如下
var inorderTraversal = function(root) {
let res = []
var getFun = function (root) {
if (!root) return
root.left && getFun(root.left)
res.push(root.val)
root.right && getFun(root.right)
}
getFun(root)
return res
};
- 同样,中序遍历也有迭代的算法
var inorderTraversal = function(root) {
let res = []
let stack = []
while (root || stack.length) {
// 先把所有左边界压入栈中,然后一个个弹出
while(root) {
stack.push(root)
root = root.left
}
root = stack.pop()
res.push(root.val)
root = root.right
}
return res
};
时间和空间复杂度均和前序遍历相同
145. 二叉树的后序遍历
- 根据后序遍历的定义,每一次递归,会先遍历左子树,再遍历右子树,再打印根节点
var postorderTraversal = function(root) {
let res = []
var getFun = function(root) {
if (!root) return
root.left && getFun(root.left)
root.right && getFun(root.right)
res.push(root.val)
}
getFun(root)
return res
};
- 递归属于正向思维,而迭代属于反向思维
- 我们思考中序遍历,左右中,其实就是中右左的反向,那么我们只需要改动一下前序遍历成,然后反转数组即可
var postorderTraversal = function(root) {
let res = []
let stack = []
while (root || stack.length) {
while (root) {
res.push(root.val)
stack.push(root)
root = root.right
}
root = stack.pop()
root = root.left
}
res.reverse()
return res
};