在处理树结构时,最基础也最重要的操作之一就是遍历。常见的遍历方式主要有三种:前序、中序 和 后序(其实广义上还有层序遍历等,但这里聚焦于这三种深度优先遍历方式)。
使用 递归 实现这三种遍历非常直观且简单,但使用 非递归(即迭代)方式实现时,就需要借助栈结构,逻辑上会复杂一些,尤其是后序遍历。
🌱 递归实现
前序
var preorderTraversal = function (root) {
let res = [];
var preorder = function (node) {
if (!node) return;
res.push(node.val);
preorder(node.left);
preorder(node.right);
};
preorder(root);
return res;
};
中序
var preorderTraversal = function (root) {
let res = [];
var preorder = function (node) {
if (!node) return;
preorder(node.left);
res.push(node.val);
preorder(node.right);
};
preorder(root);
return res;
};
后序
var postorderTraversal = function (root) {
let res = [];
var preorder = function (node) {
if (!node) return;
preorder(node.left);
preorder(node.right);
res.push(node.val);
};
preorder(root);
return res;
};
从上面的解法可以看到,这三种解法,在进行值插入的地方有差异,这就是每个根节点的遍历位置。
前序:根左右
中序:左根右
后序:左右根
🔁 非递归实现
递归虽然直观,但在某些场景中并不适用(例如系统栈有限,或需要手动管理遍历过程)。非递归实现依赖栈来模拟递归调用过程。
前序
var preorderTraversal = function(root) {
let res = []
var preorder = function(node){
let stack = []
let current = node
while(current || stack.length){
while(current){
res.push(current.val)
stack.push(current)
current = current.left
}
current = stack.pop()
current = current.right
}
}
preorder(root)
return res
};
中序
var inorderTraversal = function (root) {
let res = []
let stack = []
let current = root
while (current || stack.length > 0) {
while(current){
stack.push(current)
current = current.left
}
current = stack.pop()
res.push(current.val)
current = current.right
}
return res
};
后序
var postorderTraversal = function(root) {
let res = []
let current = root
let stack = []
let prev = null
while(current || stack.length){
while(current){
stack.push(current)
current = current.left
}
current = stack[stack.length - 1]
if(!current.right || current.right === prev){
res.push(current.val)
stack.pop()
prev = current
current = null
}else{
current = current.right
}
}
return res;
};
相较于前序和中序遍历,后序遍历的非递归实现更复杂,需要额外使用 prev 指针记录上一次访问的节点。因为在访问当前节点之前,需要确保其左右子树都已访问完成:
- 如果当前节点没有右子树,或者右子树已经访问过(即
prev === current.right),就可以将当前节点加入结果集; - 否则,需要继续遍历右子树。