哪吒2的二叉树遍历大冒险:从递归到非递归的奇幻之旅
大家好,我是哪吒2里的那个风火轮少年——哪吒!今天我要带大家进入一个神奇的二叉树世界,顺便教你们如何用Java代码来遍历这个“树”。别担心,我会用最搞笑的方式带你理解这些看似复杂的概念,保证你笑得合不拢嘴,还能学到东西!
1. 前序遍历:哪吒的“先发制人”策略
递归版:哪吒的“分身术”
首先,我们来看看前序遍历的递归版。这就像哪吒使用分身术,先把自己(根节点)展示出来,然后再派分身去左边和右边继续战斗。
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preOrder(res, root);
return res;
}
public void preOrder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
res.add(root.val); // 哪吒先出场
preOrder(res, root.left); // 分身去左边
preOrder(res, root.right); // 分身去右边
}
非递归版:哪吒的“风火轮”战术
接下来是非递归版,这就像哪吒踩着风火轮,一路向前冲,遇到岔路就记下来,等会儿再回来处理。
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack();
List<Integer> res = new ArrayList<>();
TreeNode node = root;
while (node != null || !stack.empty()) {
if (node != null) {
stack.push(node);
res.add(node.val); // 哪吒先出场
node = node.left; // 风火轮向左冲
} else {
TreeNode node1 = stack.pop();
node = node1.right; // 风火轮向右冲
}
}
return res;
}
非递归版的实现思路和逻辑:
- 初始化:我们使用一个栈来保存需要处理的节点,以及一个列表来保存遍历结果。
- 遍历左子树:从根节点开始,沿着左子树一路向下,将经过的节点压入栈中,并将节点的值加入结果列表。
- 回溯处理右子树:当左子树遍历完毕后,从栈中弹出节点,处理其右子树。
- 循环处理:重复上述过程,直到栈为空且当前节点为null。
2. 中序遍历:哪吒的“稳扎稳打”策略
递归版:哪吒的“循序渐进”
中序遍历的递归版就像哪吒在战斗中稳扎稳打,先让左边的分身打完,自己再出场,最后再让右边的分身收拾残局。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inOrder(res, root);
return res;
}
public void inOrder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
inOrder(res, root.left); // 左边的分身先打
res.add(root.val); // 哪吒出场
inOrder(res, root.right); // 右边的分身收拾残局
}
非递归版:哪吒的“迂回战术”
非递归版的中序遍历就像哪吒在战场上迂回前进,遇到敌人先记下来,等左边的敌人清理完了再回来收拾。
public List<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack();
List<Integer> res = new ArrayList<>();
TreeNode node = root;
while (node != null || !stack.empty()) {
if (node != null) {
stack.push(node); // 记下敌人
node = node.left; // 先清理左边的敌人
} else {
TreeNode node1 = stack.pop();
res.add(node1.val); // 哪吒出场
node = node1.right; // 清理右边的敌人
}
}
return res;
}
非递归版的实现思路和逻辑:
- 初始化:同样使用一个栈来保存需要处理的节点,以及一个列表来保存遍历结果。
- 遍历左子树:从根节点开始,沿着左子树一路向下,将经过的节点压入栈中。
- 回溯处理当前节点:当左子树遍历完毕后,从栈中弹出节点,将其值加入结果列表。
- 处理右子树:处理当前节点的右子树。
- 循环处理:重复上述过程,直到栈为空且当前节点为null。
3. 后序遍历:哪吒的“后发制人”策略
递归版:哪吒的“最后出场”
后序遍历的递归版就像哪吒在战斗中最后出场,先让左右的分身打完,自己再华丽登场。
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postOrder(res, root);
return res;
}
public void postOrder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
postOrder(res, root.left); // 左边的分身先打
postOrder(res, root.right); // 右边的分身收拾残局
res.add(root.val); // 哪吒最后出场
}
非递归版:哪吒的“逆袭战术”
非递归版的后序遍历就像哪吒在战场上逆袭,先记下敌人,等右边的敌人清理完了再回来收拾。
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
Stack<TreeNode> stack = new Stack();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node); // 记下敌人
res.addFirst(node.val); // 哪吒最后出场
node = node.right; // 先清理右边的敌人
} else {
node = stack.pop();
node = node.left; // 清理左边的敌人
}
}
return res;
}
非递归版的实现思路和逻辑:
- 初始化:使用一个栈来保存需要处理的节点,以及一个链表来保存遍历结果(这里使用链表是为了方便在头部插入结果)。
- 遍历右子树:从根节点开始,沿着右子树一路向下,将经过的节点压入栈中,并将节点的值插入结果链表的头部。
- 回溯处理左子树:当右子树遍历完毕后,从栈中弹出节点,处理其左子树。
- 循环处理:重复上述过程,直到栈为空且当前节点为null。
结语:坚持的意义
通过这次二叉树遍历的冒险,我们不仅学会了如何用Java代码实现前序、中序和后序遍历,还体会到了哪吒在战斗中的智慧和策略。无论是递归还是非递归,每一种方法都有其独特的魅力和适用场景。
就像哪吒在战斗中始终坚持自己的信念,我们在学习和工作中也要坚持不懈,勇于探索新的方法和技术。只有这样,我们才能在技术的道路上越走越远,最终成为真正的“技术大神”!
好了,今天的冒险就到这里,希望大家在笑声中有所收获。下次再见,记得带上你的风火轮哦!