三种常见的二叉树遍历方法
- 前序遍历
- 遍历方式:
根->左->右,先输出根节点,再前序遍历左节点,再前序遍历右节点
- 遍历方式:
- 中序遍历
- 遍历方式:
左->根->右,先中序遍历左节点,再输出根节点,再中序右节点
- 遍历方式:
- 后序遍历
- 遍历方式:
左->右->根,先后序左节点,再后序右节点,再输出根节点
- 遍历方式:
给个列子
- 前序遍历的结果是:
[1, 2, 4, 7, 5, 3, 6] - 中序遍历的结果是:
[7, 3, 2, 5, 1, 3, 6] - 后序遍历的结果是:
[7, 4, 5, 2, 6, 3, 1]
根据遍历的方式:可以看出递归的方式很容易的得到遍历的结果
递归
前序遍历
function preorder(root, ret = []) {
// 边界情况
if(root === null) return ret
// 先输出根节点
ret.push(root.val)
// 前序遍历左节点
preorder(root.left, ret)
// 前序遍历右节点
preorder(root.right, ret)
return ret
}
中序遍历
function inorder(root, ret = []) {
if (root === null) return ret
// 中序遍历左节点
inorder(root.left, ret)
// 输出根节点
ret.push(root.val)
// 中序遍历右节点
inorder(root.right, ret)
return ret
}
后序遍历
function postorder(root, ret = []) {
if (root === null) return ret
// 后序遍历左节点
postorder(root.left, ret)
// 后序遍历右节点
postorder(root.right, ret)
// 输出根节点
ret.push(root.val)
return ret
}
进阶: 迭代
迭代的方式
前序遍历
function preorder(root) {
if (root === null) return []
const ret = []
const stack = []
stack.push(root)
while(stack.length) {
// 先把根节点取出来
const node = stack.pop()
// 输出根节点
ret.push(node.val)
// 这里每次我们pop出,我们要先获取输出left, 因此需要先push right 再push left
if(node.right) stack.push(node.right)
if(node.left) stack.push(node.left)
}
return ret
}
中序遍历
一开始前序迭代的解法比较容易,结果试了下中序,卡住了,特意做了下动画演示下我们的流程
动画做的有点问题, 右节点的时候同样是入栈,动画没有修改过来,就放到旁边了
文字解析
- 我们需要的遍历的顺序是
左 -> 根 -> 右, 所以如果我们的先深度找到最左的节点的节点 - 按照中序遍历输出,我们就要记录遍历过程中的根节点
- 输出完当前根节点,如果有右节点,我们再去把右节点迭代放入我们的栈中, 再依次输出
代码
// root 遍历过程中是当前节点,也可以用一个新的curNode来替代
function inorder(root) {
const ret = []
const stack = []
while(stack.length || root) {
// 先遍历到**最左**节点
while(root) {
stack.push(root)
root = root.left
}
// 输出我们单前的左节点
root = stack.pop()
ret.push(root.val)
// 再 去指向我们的右节点,遍历入栈, 如果没有右节点会pop出上一层的节点
root = root.right
}
return ret
}
后序遍历
- 和中序遍历的大致一样
- 区分点: 当当前节点存在right的时候, 先把当前节点的文本域入栈(要不会出现死循环), 然后是当前节点指向right,进行后序遍历
const stack = []
const ret = []
while (root || stack.length) {
while (root) {
stack.push(root)
root = root.left
}
root = stack.pop()
if (root.right) {
stack.push({ val: root.val })
} else {
ret.push(root.val)
}
root = root.right
}
return ret
总结
-
递归:按照遍历的顺序比较简单
-
迭代:中序遍历 需要对当前节点进行一个过滤从而你达到先输出的节点,
-
后序遍历: 在存在right的时候需要先把单前节点入栈,在对右节点进行遍历
-
制作动画消耗了很长时间