挑战刷leetcode第14天(二叉树遍历-先中后序)

127 阅读5分钟

哪吒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;
}

非递归版的实现思路和逻辑:

  1. 初始化:我们使用一个栈来保存需要处理的节点,以及一个列表来保存遍历结果。
  2. 遍历左子树:从根节点开始,沿着左子树一路向下,将经过的节点压入栈中,并将节点的值加入结果列表。
  3. 回溯处理右子树:当左子树遍历完毕后,从栈中弹出节点,处理其右子树。
  4. 循环处理:重复上述过程,直到栈为空且当前节点为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;
}

非递归版的实现思路和逻辑:

  1. 初始化:同样使用一个栈来保存需要处理的节点,以及一个列表来保存遍历结果。
  2. 遍历左子树:从根节点开始,沿着左子树一路向下,将经过的节点压入栈中。
  3. 回溯处理当前节点:当左子树遍历完毕后,从栈中弹出节点,将其值加入结果列表。
  4. 处理右子树:处理当前节点的右子树。
  5. 循环处理:重复上述过程,直到栈为空且当前节点为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;
}

非递归版的实现思路和逻辑:

  1. 初始化:使用一个栈来保存需要处理的节点,以及一个链表来保存遍历结果(这里使用链表是为了方便在头部插入结果)。
  2. 遍历右子树:从根节点开始,沿着右子树一路向下,将经过的节点压入栈中,并将节点的值插入结果链表的头部。
  3. 回溯处理左子树:当右子树遍历完毕后,从栈中弹出节点,处理其左子树。
  4. 循环处理:重复上述过程,直到栈为空且当前节点为null。

结语:坚持的意义

通过这次二叉树遍历的冒险,我们不仅学会了如何用Java代码实现前序、中序和后序遍历,还体会到了哪吒在战斗中的智慧和策略。无论是递归还是非递归,每一种方法都有其独特的魅力和适用场景。

就像哪吒在战斗中始终坚持自己的信念,我们在学习和工作中也要坚持不懈,勇于探索新的方法和技术。只有这样,我们才能在技术的道路上越走越远,最终成为真正的“技术大神”!

好了,今天的冒险就到这里,希望大家在笑声中有所收获。下次再见,记得带上你的风火轮哦!