二叉树——遍历

1. 二叉树遍历

1.1. 问题

分别用递归、非递归的方式实现二叉树的前序、中序、后序遍历。

1.2. 思路

递归的方式就不用说了,很简单。

对于非递归的方式,我们需要用一个栈来模拟,递归函数的方法嵌套。

非递归的前序遍历还挺容易写出来的,在中序遍历这里我卡壳了。

最后看书,发现是用一个cur游标来指向当前需要压栈的节点。卡壳的时候想用stack的peek节点来压left子节点。当left子节点为空就弹出stack的节点,消费之。正常情况时,消费节点后接下来需要压消费节点的右孩子节点,如果只用stack的peek节点来弄的话就区分不了右孩子节点的情况。因此引入一个cur游标指向当前需要压栈的节点,当消费一个节点后,就自动将cur交给它的右孩子节点。

然后后序遍历也花了我好长一段时间思考。书上给了两个版本的代码,一个版本是用了两个stack,一个版本用了一个stack。我写的是后一个版本。这道题就需要使用stack的peek信息,然后用pre节点表示上一个消费过的节点,这是由于后序遍历中,每个子树的根节点都是在其子树中最后访问到的。

而中序遍历就不能用pre,因为pre可能是当前节点的左子树的最后一个节点,跟当前节点没有直接关系,我卡壳的时候也想过用pre,发现还是解决不了,就一时没想到可以用cur表示当前需要压栈的节点。

其实,不需要死记代码的,只要先在纸上模拟一下入栈出栈的过程,就能把代码写出来了。

时间复杂度是O(n),空间复杂度为O(h),h为二叉树的层高。

理论上讲,所有的递归都可以用循环的方式写(手动模拟出栈入栈),因此这里的非递归的方式,本质上跟递归的方式没啥区别。

然后还有一种线索化的遍历方式:Morris遍历,可以将空间复杂度优化成常量时间复杂度。

1.3. 代码

1.3.1. 前序遍历

public static <T> void preOrder(TreeNode<T> root, Consumer<T> consumer) {
if (root == null) return;
Stack<TreeNode<T>> stack = new Stack<>();
stack.push(root);
while (!stack.empty()) {
TreeNode<T> node = stack.pop();
consumer.accept(node.val);
if (node.right != null)
stack.push(node.right);
if (node.left != null)
stack.push(node.left);
}
}

1.3.2. 中序遍历

public static <T> void midOrder(TreeNode<T> root, Consumer<T> consumer) {
if (root == null) return;
Stack<TreeNode<T>> stack = new Stack<>();
//表示当前需要遍历的二叉(子)树的根节点
TreeNode<T> cur = root;
//栈中没有待完成遍历的二叉树(子树)根节点,并且当前也没有需要遍历的二叉树的根节点,则遍历结束。
while (!stack.empty() || cur != null) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
TreeNode<T> node = stack.pop();
consumer.accept(node.val);
cur = node.right;
}
}
}

1.3.3. 后序遍历

public static <T> void postOrder(TreeNode<T> root, Consumer<T> consumer) {
if (root == null) return;
Stack<TreeNode<T>> stack = new Stack<>();
//注意这里不能是null
TreeNode<T> pre = new TreeNode<>();
stack.push(root);
while (!stack.empty()) {
TreeNode<T> peek = stack.peek();
if (peek.left != null && peek.left != pre && peek.right != pre) {
//之前漏条件了:speek.right != pre
stack.push(peek.left);
} else if (peek.right != null && peek.right != pre) {
//之前条件写错了,后面的条件写成了:peek.left == pre
stack.push(peek.right);
} else {
TreeNode<T> node = stack.pop();
consumer.accept(node.val);
pre = node;
}
}
}
posted @   迈吉  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示