本文已参与「新人创作礼」活动,一起开启掘金创作之路。
二叉树的递归遍历比较简单,但是在遍历节点较多的二叉树时有可能会栈溢出,所以还是非递归更安全
递归的本质还是栈,用栈模仿递归的过程即可
前序遍历
对于每个节点来说,先遍历节点值,再遍历左子树,最后遍历右子树
- 首先将当前遍历位置设为根节点
- 在当前遍历位置不为空或栈不为空时(表示递归还未结束)进行循环
- 每次循环时,将当前节点加入遍历结果并且放入栈,然后将当前节点指向当前节点左儿子,重复这个过程直至当前节点为空(表示没有左儿子了);此时栈顶元素即为还未遍历的最左节点,将其出栈,并将当前节点指向出栈节点右儿子(类似递归遍历左子树结束后,进入右子树递归)
具体看代码
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
if (node != null) {
// 优先遍历节点值
ans.add(node.val);
// 当前节点入栈,类似存下递归的上层
stack.push(node);
// 遍历左子树
node = node.left;
}
// 左子树为空,栈顶元素出栈,类似返回上层递归
node = stack.pop();
// 遍历右子树
node = node.right;
}
return ans;
}
中序遍历
遍历顺序为 左子树 -> 本节点值 -> 右子树
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
while (node != null) {
// 当前节点入栈,类似存下递归的上层
stack.push(node);
// 优先遍历左子树
node = node.left;
}
// 左子树为空时,栈顶元素出栈,返回上层递归
node = stack.pop();
// 遍历上层递归节点值
ans.add(node.val);
// 最后遍历右子树
node = node.right;
}
return ans;
}
后序遍历
遍历顺序为 左子树 -> 右子树 -> 本节点值
需要注意,后序和前两个不同,需要额外处理右子树遍历的问题,具体看代码
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode node = root;
TreeNode prev = null; // 最近一次访问的节点
while (!stack.isEmpty() || node != null) {
while (node != null) {
// 当前节点入栈,类似存下递归的上层
stack.push(node);
// 优先遍历左子树
node = node.left;
}
// 栈顶元素出栈,返回上层递归
node = stack.pop();
// 如果右子树不为空且不是上次遍历过的位置
if (node.right != null && node.right != prev) {
// 放入当前节点,类似存下递归的上层
stack.push(node);
// 遍历右子树
node = node.right;
} else {
// 存下最近一次遍历的节点
prev = node;
// 遍历当前节点值
ans.add(node.val);
// 置空,防止重复进入左子树
node = null;
}
}
return ans;
}