0
点赞
收藏
分享

微信扫一扫

【新人系列】Python 入门(十四):文件操作

文章目录

二叉树遍历

前、中、后序遍历

  • 前序:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/

在这里插入图片描述

在这里插入图片描述

  • 中序:https://leetcode.cn/problems/binary-tree-inorder-traversal/description/

在这里插入图片描述

  • 后序:https://leetcode.cn/problems/binary-tree-postorder-traversal/description/

在这里插入图片描述

在这里插入图片描述

递归法求解

三种遍历,只是节点取值顺序有调整而已:

  • 前序:取值 | 遍历左子树 | 遍历右子树
  • 中序:遍历左子树 | 取值 | 遍历右子树
  • 后序:遍历左子树 | 遍历右子树 | 取值
前序遍历
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        traversal(root, result);
        return result;
    }

    private void traversal(TreeNode cur, List<Integer> result) {
        if (cur == null) return;
        result.add(cur.val);
        traversal(cur.left, result);
        traversal(cur.right, result);
    }
}
中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        traversal(root, result);
        return result;
    }

    private void traversal(TreeNode cur, List<Integer> result) {
        if (cur == null)
            return;
        traversal(cur.left, result);
        result.add(cur.val);
        traversal(cur.right, result);
    }
}
后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        traversal(root, result);
        return result;
    }

    private void traversal(TreeNode cur, List<Integer> result) {
        if (cur == null)
            return;
        traversal(cur.left, result);
        traversal(cur.right, result);
        result.add(cur.val);
    }
}

迭代遍历法求解

前序遍历※
public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode pop = stack.pop();
        result.add(pop.val);
        // 如果当前取出的节点,存在左右节点,则将其存储到栈中。
        // 注意,先存储右节点,再存储左节点,因为栈的特性是先进后出
        if (pop.right != null) stack.push(pop.right);
        if (pop.left != null) stack.push(pop.left); 
    }
    return result;
}

代码执行过程可以参考如下图片,每个红框表示一次循环的操作
在这里插入图片描述

中序遍历※

思路:

1、初始化栈:创建一个空栈。

2、遍历左子树:从根节点开始,如果当前节点有左子节点,则将当前节点压入栈中,并将当前节点设置为其左子节点,继续遍历左子树,直到到达叶子节点。

3、处理节点:当没有左子节点时,从栈中弹出一个节点,访问该节点(例如,将其值添加到结果集合中)。

4、遍历右子树:检查刚刚弹出的节点是否有右子节点。如果有,将当前节点设置为该右子节点,并返回第2步继续遍历;如果没有,继续从栈中弹出节点,重复第3步,直到栈为空或弹出的节点有右子节点。

在这里插入图片描述

初始实现:

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode cur = root;
    stack.push(cur);
    while (cur != null && !stack.isEmpty()) {
        if (cur.left != null) {
            // --if-- 如果当前节点还有左节点,将其加入栈中
            stack.push(cur.left);
            cur = cur.left;
        }else {
            // 从栈中不断弹出元素,直到弹出的元素,右节点不为空
            while (!stack.isEmpty()) {
                TreeNode pop = stack.pop();
                result.add(pop.val);
                if (pop.right != null) {
                    // --if-- 当前节点有右节点,开始遍历右节点
                    stack.push(pop.right);
                    cur = pop.right;
                    break;
                }
            }
        }
    }
    return result;
}

优化代码:

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode cur = root;

    while (cur != null || !stack.isEmpty()) {
        // 首先,将所有左子节点入栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }

        // 当前节点为空,说明到达叶子节点的左侧,开始出栈并处理节点
        cur = stack.pop();
        result.add(cur.val);

        // 处理右子树
        cur = cur.right;
    }
    return result;
}

代码随想录版本:

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) {
        return result;
    }
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        if (cur != null) {
            stack.push(cur);
            cur = cur.left;
        } else {
            cur = stack.pop();
            result.add(cur.val);
            cur = cur.right;
        }
    }
    return result;
}
后序遍历※

前序遍历是中左右,后续遍历是左右中。只需要先将前序遍历的代码改成中右左,再将得到的result反转即可。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode pop = stack.pop();
        result.add(pop.val);
        // 这里要模拟中右左,先存储左节点,再存储右节点,因为栈的特性是先进后出
        if (pop.left != null) stack.push(pop.left);
        if (pop.right != null) stack.push(pop.right);
    }
    Collections.reverse(result);
    return result;
}

层序遍历

102. 二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal/description/

在这里插入图片描述

迭代法

在这里插入图片描述

按照上面的方式可以将节点的数值按照层遍历出来了,但是题目要求结果需要分层来存储。因此我使用一个变量来记录下一层的节点数量,这样循环下层的时候,可以知道下层的节点数量,进而将该层的节点全部从队列中取出来

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> deque = new ArrayDeque<>();
    deque.addFirst(root);
    int num = 1;
    // 用来记录下层的节点数量
    int addNum;
    while (!deque.isEmpty()) {
        List<Integer> layer = new ArrayList<>();
        addNum = 0;
        while (num-- > 0) {
            // 把当前层的节点全部取出来
            TreeNode treeNode = deque.pollLast();
            layer.add(treeNode.val);
            if (treeNode.left != null) {
                deque.addFirst(treeNode.left);
                // 下层的节点数量++
                addNum++;
            }
            if (treeNode.right != null) {
                deque.addFirst(treeNode.right);
                // 下层的节点数量++
                addNum++;
            }
        }
        result.add(layer);
        num = addNum;
    }
    return result;
}

在这里插入图片描述

如果使用LinkedList来作为队列的话,就不需要记录下一层的节点数了,因为LinkedList提供了.size()方法

 public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        List<Integer> layer = new ArrayList<>();
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            layer.add(treeNode.val);
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        result.add(layer);
    }
    return result;
}
递归法
public List<List<Integer>> levelOrder2(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    recursion(result, root, 0);
    return result;
}

private void recursion(List<List<Integer>> result, TreeNode node, int deep) {
    if (node == null) return;
    if (deep >= result.size()) {
        result.add(new ArrayList<>());
    }
    result.get(deep).add(node.val);
    recursion(result, node.left, ++deep);
    recursion(result, node.right, deep);
}

在这里插入图片描述

107. 二叉树的层序遍历 II

https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/

在这里插入图片描述

和上一道题的做法几乎是一致的,最后把结果翻转一下就行

public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        List<Integer> layer = new ArrayList<>();
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            layer.add(treeNode.val);
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        // 唯一的区别
        result.add(0,layer);
    }
    return result;
}

199. 二叉树的右视图

https://leetcode.cn/problems/binary-tree-right-side-view/description/

给大家介绍一个非常好用的画二叉树的网站:https://csacademy.com/app/graph_editor/

在这里插入图片描述

在这里插入图片描述

直接使用层序遍历的代码,每层值保存最后一个值即可

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    int num = 0;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            num = treeNode.val;
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        result.add(num);
    }
    return result;
}

在这里插入图片描述

637.二叉树的层平均值

https://leetcode.cn/problems/average-of-levels-in-binary-tree/description/

在这里插入图片描述

在这里插入图片描述

每层计算一下平均值即可

public List<Double> averageOfLevels(TreeNode root) {
    List<Double> result = new ArrayList<>();
    if (root == null) return result;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        double sum = 0;
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            sum += treeNode.val;
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        result.add(sum / layerNum);
    }
    return result;
}

在这里插入图片描述

429. N 叉树的层序遍历

https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/

在这里插入图片描述

public List<List<Integer>> levelOrder(Node root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    LinkedList<Node> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        List<Integer> layer = new ArrayList<>();
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            Node treeNode = queue.pollLast();
            layer.add(treeNode.val);
            for (Node child : treeNode.children) {
                if (child != null) {
                    queue.addFirst(child);
                }
            }
        }
        result.add(layer);
    }
    return result;
}

在这里插入图片描述

116. 填充每个节点的下一个右侧节点指针

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/description/

在这里插入图片描述

在这里插入图片描述

public Node connect(Node root) {
    if (root == null) return root;
    LinkedList<Node> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        Node lastNode = null;
        for (int i = 0; i < layerNum ; i++) {
            Node treeNode = queue.pollLast();
            if (lastNode != null) {
                lastNode.next = treeNode;
            }
            lastNode = treeNode;
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
    }
    return root;
}

在这里插入图片描述

117. 填充每个节点的下一个右侧节点指针 II

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/description/

在这里插入图片描述

代码和上一道题一模一样

在这里插入图片描述

  1. 二叉树的最大深度

https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/

在这里插入图片描述

遍历法

每遍历一层,深度+1

public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    int depth = 0;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        depth++;
    }
    return depth;
}

在这里插入图片描述

递归法
public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return dfs(root, 1);
}

private int dfs(TreeNode cur, int depth) {
    if (cur.left != null && cur.right != null) {
        return Math.max(dfs(cur.left, depth + 1), dfs(cur.right, depth + 1));
    } else if (cur.left != null) {
        return dfs(cur.left, depth + 1);
    } else if (cur.right != null) {
        return dfs(cur.right, depth + 1);
    } else {
        return depth;
    }
}

递归法效率高一点

在这里插入图片描述

111. 二叉树的最小深度

https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/

在这里插入图片描述

进行层序遍历的时候,一旦找到任何一层有叶子节点,说明该层的深度是最小深度

public int minDepth(TreeNode root) {
    if (root == null) return 0;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    int depth = 0;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        boolean isStop = false;
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
            // 找到叶子节点,说明该层是最小深度
            if (treeNode.left == null && treeNode.right == null) {
                isStop = true;
                break;
            }
        }
        depth++;
        if (isStop == true) {
            break;
        }
    }
    return depth;
}

在这里插入图片描述

morris遍历(了解即可)

翻转二叉树

https://leetcode.cn/problems/invert-binary-tree/description/

在这里插入图片描述

递归法

这道题使用前序遍历或者后续遍历都很容易解决,其实就是遍历的时候,反转一下所遍历节点的左右子节点即可

【前序遍历】

  • 前序:交换左右子节点 | 遍历左子树 | 遍历右子树
public TreeNode invertTree(TreeNode root) {
    dfs(root);
    return root;
}

private void dfs(TreeNode node) {
    if (node == null) {
        return;
    }
    // 交换左右子节点
    TreeNode temp = node.left;
    node.left = node.right;
    node.right = temp;
    // 遍历左子树
    dfs(node.left);
    // 遍历右子树
    dfs(node.right);
}

在这里插入图片描述

【后序遍历】

  • 后序: 遍历左子树 | 遍历右子树 | 交换左右子节点
public TreeNode invertTree(TreeNode root) {
    dfs(root);
    return root;
}

private void dfs(TreeNode node) {
    if (node == null) {
        return;
    }
    dfs(node.left);
    dfs(node.right);
    TreeNode temp = node.left;
    node.left = node.right;
    node.right = temp;
}

【中序】

中序会产生什么问题呢?

一开始递归搜索了left,然后交换之后,right变成了left,最后再递归right,其实本质也是递归left

dfs(node.left);

TreeNode temp = node.left;
node.left = node.right;
node.right = temp;

dfs(node.right);

因此如果需要使用中序遍历,需要在交换之后,还是对left进行递归

class Solution {
    public TreeNode invertTree(TreeNode root) {
        dfs(root);
        return root;
    }

    private void dfs(TreeNode node) {
        if (node == null) {
            return;
        }
    
        dfs(node.left);
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
        dfs(node.left);
    }
}

迭代法

在层序遍历的基础上,交换一下左右子节点即可

public TreeNode invertTree1(TreeNode root) {
    if (root == null) return root;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    while (!queue.isEmpty()) {
        List<Integer> layer = new ArrayList<>();
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            layer.add(treeNode.val);
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
            // 交换左右子节点
            TreeNode temp = treeNode.left;
            treeNode.left = treeNode.right;
            treeNode.right = temp;
        }
    }
    return root;
}

对称二叉树

https://leetcode.cn/problems/symmetric-tree/description/

在这里插入图片描述

在这里插入图片描述

递归法

【字符串记录树的值】

root的左右子树分别递归,用字符串记录树的值

public boolean isSymmetric(TreeNode root) {
    StringBuilder s1 = new StringBuilder();
    StringBuilder s2 = new StringBuilder();
    leftDfs(s1, root.left);
    rightDfs(s2, root.right);
    System.out.println(s1.toString());
    System.out.println(s2.toString());
    return s1.toString().equals(s2.toString());
}

private void leftDfs(StringBuilder s, TreeNode node) {
    if (node == null) {
        s.append("n");
        return;
    }
    s.append(node.val);
    leftDfs(s, node.left);
    leftDfs(s, node.right);
}

private void rightDfs(StringBuilder s, TreeNode node) {
    if (node == null) {
        s.append("n");
        return;
    }
    s.append(node.val);
    rightDfs(s, node.right);
    rightDfs(s, node.left);
}

效率很低

在这里插入图片描述

【两颗子树一起递归】

public boolean isSymmetric(TreeNode root) {
    return dfs(root.left, root.right);
}

private boolean dfs(TreeNode left, TreeNode right) {
    if (left != null && right != null) {
        // --if-- left和right都不为null,判断当前两个节点的值是否相同,并进一步判断节点的子节点是否相同
        return left.val == right.val && dfs(left.left, right.right) && dfs(left.right, right.left);
    } else if (left == right) {
        // --if-- left和right都是null
        return true;
    } else {
        // --if-- left和right一个为null,一个不为null,肯定返回false
        return false;
    }
}

在这里插入图片描述

迭代法

public boolean isSymmetric(TreeNode root) {
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.addFirst(root.left);
    queue.addFirst(root.right);
    while (!queue.isEmpty()) {
        TreeNode node1 = queue.pollLast();
        TreeNode node2 = queue.pollLast();
        if (node1 == node2) {
            // --if-- 两个节点都是null
            continue;
        } else if (node1 != null && node2 != null && node1.val == node2.val) {
            // --if-- 两个节点都不为null,且值相同
            queue.addFirst(node1.left);
            queue.addFirst(node2.right);
            queue.addFirst(node1.right);
            queue.addFirst(node2.left);
            continue;
        }
        // 有一个节点为null,或两个节点值相同
        return false;
    }
    return true;
}

在这里插入图片描述

完全二叉树的节点个数

https://leetcode.cn/problems/count-complete-tree-nodes/description/

在这里插入图片描述

递归法

public int countNodes(TreeNode node) {
    if (node == null) {
        return 0;
    }
    // 当前节点计数为1
    int num = 1;
    // 加上左子树的节点数量
    num += countNodes(node.left);
    // 加上右子树的节点数量
    num += countNodes(node.right);

    return num;
}

在这里插入图片描述

代码再简化

public int countNodes(TreeNode node) {
    if (node == null) {
        return 0;
    }
    // 当前节点计数为1+左子树的节点数量+右子树的节点数量
    return 1 + countNodes(node.left) + countNodes(node.right);
}

迭代法

public int countNodes(TreeNode root) {
    if (root == null) return 0;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);
    int num = 0;
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode pop = stack.pop();
        num++;
        // 如果当前取出的节点,存在左右节点,则将其存储到栈中。
        // 注意,先存储右节点,再存储左节点,因为栈的特性是先进后出
        if (pop.right != null) stack.push(pop.right);
        if (pop.left != null) stack.push(pop.left);
    }
    return num;
}

在这里插入图片描述

平衡二叉树

https://leetcode.cn/problems/balanced-binary-tree/description/

高度平衡二叉树定义:该二叉树任意节点的左右两个子树的高度差的绝对值不超过1。

在这里插入图片描述

在这里插入图片描述

递归法 ※

该题可以借鉴二叉树的最大深度,当左右子树不平衡的时候,可以返回-1

public boolean isBalanced(TreeNode root) {
    return recursion(root, 0) != -1;
}

private int recursion(TreeNode cur, int depth) {
    if (cur == null) {
        return depth;
    }
    // 计算左子树的深度
    int leftDepth = recursion(cur.left, depth + 1);
    if (leftDepth == -1) {
        // 左子树不是平衡二叉树,返回-1
        return -1;
    }
    // 计算右子树的深度
    int rightDepth = recursion(cur.right, depth + 1);
    if (rightDepth == -1) {
        // 右子树不是平衡二叉树,返回-1
        return -1;
    }
    if (leftDepth < rightDepth - 1 || leftDepth > rightDepth + 1) {
        // 如果左右子树深度差 大于 1,说明当前节点的树为非平衡二叉树
        return -1;
    }
    return Math.max(leftDepth, rightDepth);
}

在这里插入图片描述

迭代法

其实就是两个层序遍历,一个用来求高度,还有一个则用来调用求高度方法来判断左右节点高度是否相差大于1。该方式效率较低,节点被遍历了多次(每次求高度都需要遍历子树的所有节点),时间复杂度O(n^2)

public boolean isBalanced1(TreeNode root) {
    if (root == null) return true;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int layerNum;
    int depth = 0;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            if (Math.abs(getHeight(treeNode.left) - getHeight(treeNode.right)) > 1) {
                return false;
            }
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        depth++;
    }
    return true;
}

public int getHeight(TreeNode node) {
    if (node == null) return 0;
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(node);
    int layerNum;
    int depth = 0;
    while (!queue.isEmpty()) {
        // 当前层的节点数量
        layerNum = queue.size();
        for (int i = 0; i < layerNum; i++) {
            TreeNode treeNode = queue.pollLast();
            if (treeNode.left != null) {
                queue.addFirst(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.addFirst(treeNode.right);
            }
        }
        depth++;
    }
    return depth;
}

在这里插入图片描述

优化版本

  • 针对暴力迭代法的getHeight方法做优化,利用TreeNode.val(这个字段本身没有被使用)来保存当前节点的高度,这样就不会有重复遍历
  • 获取高度算法时间复杂度可以降到O(1),总的时间复杂度降为O(n)

【算法思路】

  • 有左节点,先遍历左节点
  • 左节点遍历完成,有右节点的话,再遍历右节点
  • 左右节点都遍历完成之后,计算左右子树的高度差,判断是否为平衡二叉树
public boolean isBalanced(TreeNode root) {
    if (root == null) {
        return true;
    }
    Stack<TreeNode> stack = new Stack<>();
    // 记录上一个遍历到的节点
    TreeNode pre = null;
    while (root != null || !stack.isEmpty()) {
        while (root != null) {
            stack.push(root);
            root = root.left;
        }
        // 这里并没有弹出,只是取出来,等计算完该节点的左右子树高度差之后,才会弹出
        TreeNode curNode = stack.peek();
        // 右节点为null或已经遍历过
        if (curNode.right == null || curNode.right == pre) {
            // 输出
            if (Math.abs(getHeight(curNode.left) - getHeight(curNode.right)) > 1) {
                return false;
            }
            // 计算完高度差之后,再将当前遍历节点弹出
            stack.pop();
            // 记录所遍历到的节点
            pre = curNode;
            // 当前节点下,没有要遍历的节点了,因为右节点也遍历过了
            root = null;
        } else {
            // 右节点还没遍历,遍历右节点
            root = curNode.right;
        }
    }
    return true;
}

/**
 * 求节点的高度
 * 计算当前节点高度的前提是,当前节点的左右子树高度已经计算过,所以使用中序遍历
 */
public int getHeight(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int leftHeight = root.left != null ? root.left.val : 0;
    int rightHeight = root.right != null ? root.right.val : 0;
    // 用TreeNode.val来保存当前节点的高度
    root.val =  Math.max(leftHeight, rightHeight) + 1;
    return root.val;
}

如果看代码有点懵逼,可以看下面图的迭代流程,每个蓝框都是一次循环

在这里插入图片描述

二叉树的所有路径

https://leetcode.cn/problems/binary-tree-paths/description/

在这里插入图片描述

递归法

public List<String> binaryTreePaths(TreeNode root) {
    List<String> res = new ArrayList<>();
    recursion(root, res, "");
    return res;
}

private void recursion(TreeNode node, List<String> res, String str) {
    if (node != null) {
        str += "->" + node.val;
        if (node.left == null && node.right == null) {
            res.add(str.substring(2));
        }
        recursion(node.left, res, str);
        recursion(node.right, res, str);
    }
}

在这里插入图片描述

因为上面使用的是String来拼接路径,效率比较低

【改进】

public List<String> binaryTreePaths1(TreeNode root) {
    List<String> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    recursion1(root, res, path);
    return res;
}

private void recursion1(TreeNode node, List<String> res, List<Integer> path) {
    if (node != null) {
        path.add(node.val);
        if (node.left == null && node.right == null) {
            // --if-- 当前节点已经是叶子节点,存储路径
            // 使用StringBuilder拼接比直接使用String拼接更快
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < path.size() - 1; i++) {
                stringBuilder.append(path.get(i)).append("->");
            }
            stringBuilder.append(path.get(path.size() - 1));
            res.add(stringBuilder.toString());
        }
        recursion1(node.left, res, path);
        recursion1(node.right, res, path);
//            System.out.println(path.toString());
        // 删掉当前递归深度添加的数字
        path.remove(path.size() - 1);
    }
}

在这里插入图片描述

为什么要删除最后一个节点,假如不删除的话,下图的结果是1->2->51->2->5->3

在这里插入图片描述

假如放开上面的输出,输出结果如下

[1]
[1, 2]
[1, 2, 5]
[1, 3]

递归的过程如图所示

在这里插入图片描述

迭代法

在前序遍历迭代法的基础上面修改,使用栈同时记录节点和节点对应的路径

public List<String> binaryTreePaths2(TreeNode root) {
    List<String> result = new ArrayList<>();
    if (root == null) return result;
    Deque<Object> stack = new ArrayDeque<>();
    stack.push(root);
    stack.push(root.val + "");
    while (!stack.isEmpty()) {
        String path = (String) stack.pop();
        TreeNode cur = (TreeNode) stack.pop();
        if (cur != null) {
            if (cur.left == null && cur.right == null) {
                result.add(path);
            }
            // 如果当前取出的节点,存在左右节点,则将其存储到栈中。
            // 注意,先存储右节点,再存储左节点,因为栈的特性是先进后出
            if (cur.right != null) {
                stack.push(cur.right);
                stack.push(path + "->" + cur.right.val);
            }
            if (cur.left != null) {
                stack.push(cur.left);
                stack.push(path + "->" + cur.left.val);
            }
        }
    }
    return result;
}

在这里插入图片描述
在这里插入图片描述

相同的树

https://leetcode.cn/problems/same-tree/

在这里插入图片描述

在这里插入图片描述

递归法

解法和对称二叉树基本是一样的,树1的左和树2的左比,树1的右和树2的右比就行

public boolean isSameTree(TreeNode node1, TreeNode node2) {
    if (node1 != null && node2 != null) {
        // --if-- node1和node2都不为null,判断当前两个节点的值是否相同,并进一步判断节点的子节点是否相同
        return node1.val == node2.val && isSameTree(node1.left, node2.left) && isSameTree(node1.right, node2.right);
    } else if (node1 == node2) {
        // --if-- node1和node2都是null
        return true;
    } else {
        // --if-- node1和node2一个为null,一个不为null,肯定返回false
        return false;
    }
}

在这里插入图片描述

迭代法

public boolean isSameTree(TreeNode node1, TreeNode node2) {
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.addFirst(node1);
    queue.addFirst(node2);
    while (!queue.isEmpty()) {
        node1 = queue.pollLast();
        node2 = queue.pollLast();
        if (node1 == node2) {
            // --if-- 两个节点都是null
            continue;
        } else if (node1 != null && node2 != null && node1.val == node2.val) {
            // --if-- 两个节点都不为null,且值相同
            queue.addFirst(node1.left);
            queue.addFirst(node2.left);
            queue.addFirst(node1.right);
            queue.addFirst(node2.right);
            continue;
        }
        // 有一个节点为null,或两个节点值相同
        return false;
    }
    return true;
}

在这里插入图片描述

左叶子之和

https://leetcode.cn/problems/sum-of-left-leaves/description/
在这里插入图片描述

递归法 ※

public int sumOfLeftLeaves(TreeNode node) {
    if (node == null) {
        return 0;
    }
    if (node.left != null && node.left.left == null && node.left.right == null) {
        // 如果当前节点的左节点不为null,但是左节点没有子节点,加上该节点的值
        return node.left.val + sumOfLeftLeaves(node.right);
    }
    return sumOfLeftLeaves(node.left) + sumOfLeftLeaves(node.right);
}

在这里插入图片描述

迭代法

public int sumOfLeftLeaves1(TreeNode root) {
    int sum = 0;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode node = stack.pop();
        if (node.left != null && node.left.left == null && node.left.right == null) {
            // 如果当前节点的左节点不为null,但是左节点没有子节点,加上该节点的值
            sum+= node.left.val;
        }
        // 如果当前取出的节点,存在左右节点,则将其存储到栈中。
        // 注意,先存储右节点,再存储左节点,因为栈的特性是先进后出
        if (node.right != null) stack.push(node.right);
        if (node.left != null) stack.push(node.left);
    }
    return sum;
}

在这里插入图片描述

找树左下角的值

https://leetcode.cn/problems/find-bottom-left-tree-value/description/

在这里插入图片描述

递归法

最简单的方法,使用全局变量,当遍历到到比targetDeep的节点时,更新targetVal的值

private int targetDeep;
private int targetVal;

public int findBottomLeftValue(TreeNode root) {
    targetDeep = -1;
    recursion(root, 0);
    return targetVal;
}

private void recursion(TreeNode cur, int deep) {
    if (cur == null) return;
    if (deep > targetDeep) {
        targetVal = cur.val;
        targetDeep = deep;
    }
    recursion(cur.left, deep + 1);
    recursion(cur.right, deep + 1);
}

在这里插入图片描述

迭代法

直接层序遍历,遍历到每层的第一个节点时,更新值即可

public int findBottomLeftValue2(TreeNode root) {
    if (root == null) return 0;
    int targetNum = 0;
    Deque<TreeNode> deque = new ArrayDeque<>();
    deque.addFirst(root);
    int num = 1;
    // 用来记录下层的节点数量
    int addNum;
    TreeNode treeNode;
    while (!deque.isEmpty()) {
        addNum = 0;
        // 为什么上面的代码不放在循环里面,就是为了少一个判断
        for (int i = 0; i < num; i++) {
            // 把当前层的节点全部取出来
            treeNode = deque.pollLast();
            if (i == 0) {
                targetNum = treeNode.val;
            }
            if (treeNode.left != null) {
                deque.addFirst(treeNode.left);
                // 下层的节点数量++
                addNum++;
            }
            if (treeNode.right != null) {
                deque.addFirst(treeNode.right);
                // 下层的节点数量++
                addNum++;
            }
        }
        num = addNum;
    }
    return targetNum;
}

在这里插入图片描述

可以做一点点优化,减少一个判断,就是代码冗余一点

public int findBottomLeftValue1(TreeNode root) {
    if (root == null) return 0;
    int targetNum = 0;
    Deque<TreeNode> deque = new ArrayDeque<>();
    deque.addFirst(root);
    int num = 1;
    // 用来记录下层的节点数量
    int addNum;
    while (!deque.isEmpty()) {
        addNum = 0;
        // 将每层的第一个节点的值存储起来
        TreeNode treeNode = deque.pollLast();
        targetNum = treeNode.val;
        if (treeNode.left != null) {
            deque.addFirst(treeNode.left);
            // 下层的节点数量++
            addNum++;
        }
        if (treeNode.right != null) {
            deque.addFirst(treeNode.right);
            // 下层的节点数量++
            addNum++;
        }
        // 为什么上面的代码不放在循环里面,就是为了少一个判断
        for (int i = 1; i < num; i++) {
            // 把当前层的节点全部取出来
            treeNode = deque.pollLast();
            if (treeNode.left != null) {
                deque.addFirst(treeNode.left);
                // 下层的节点数量++
                addNum++;
            }
            if (treeNode.right != null) {
                deque.addFirst(treeNode.right);
                // 下层的节点数量++
                addNum++;
            }
        }
        num = addNum;
    }
    return targetNum;
}

在这里插入图片描述

路径总和

https://leetcode.cn/problems/path-sum/description/

在这里插入图片描述

在这里插入图片描述

递归法

/**
 * 判断是否可以找到等于总和的路径
 * @param node
 * @param leftNum 剩余的树
 * @return
 */
public boolean hasPathSum(TreeNode node, int leftNum) {
    if (node == null) {
        return false;
    }
    if (node.left == null && node.right == null && leftNum == node.val) {
        // --if-- 如果当前节点为叶子节点,且节点值正好等于剩余数字
        return true;
    }
    // 左子树和右子树中能找到一条路径,即可返回true
    return hasPathSum(node.left, leftNum - node.val) || 
    hasPathSum(node.right, leftNum - node.val);
}

在这里插入图片描述

迭代法 ※

使用一个numStack来记录上一个节点的总和

public boolean hasPathSum1(TreeNode root, int targetNum) {
    if (root == null) return false;
    Deque<TreeNode> stack = new ArrayDeque<>();
    Deque<Integer> numStack = new ArrayDeque<>();
    stack.addFirst(root);
    numStack.addFirst(0);
    int num = 1;
    // 用来记录下层的节点数量
    int addNum;
    TreeNode treeNode;
    while (!stack.isEmpty()) {
        addNum = 0;
        // 为什么上面的代码不放在循环里面,就是为了少一个判断
        for (int i = 0; i < num; i++) {
            // 把当前层的节点全部取出来
            treeNode = stack.pollLast();
            int totalNum = numStack.pollLast();

            int curNum = totalNum + treeNode.val;
            if (treeNode.left == null && treeNode.right == null && curNum == targetNum) {
                return true;
            }
            if (treeNode.left != null) {
                stack.addFirst(treeNode.left);
                numStack.push(curNum);
                // 下层的节点数量++
                addNum++;
            }
            if (treeNode.right != null) {
                stack.addFirst(treeNode.right);
                numStack.push(curNum);
                // 下层的节点数量++
                addNum++;
            }
        }
        num = addNum;
    }
    return false;
}

在这里插入图片描述

从中序与后序遍历序列构造二叉树※

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/

在这里插入图片描述

在这里插入图片描述

思路

首先需要思考中序遍历和后续遍历的顺序。

  • 中序遍历:左中右
  • 后序遍历:左右中

从上面可以挖掘出什么样的信息呢?

  • 后序遍历的数组最后一个元素,一定是最顶层的节点,即根节点

在这里插入图片描述

  • 题目中说明了数组中没有重复的值,那我们可以用后续遍历数组的最后一个数字,来找到一个位置,来将中序遍历得到数组划分为两个部分,第一个部分是根节点的左子节点,第二个部分则是根节点的右子节点

在这里插入图片描述

  • 划分完前序遍历数组之后,还需要处理一下后续遍历数组。

在这里插入图片描述

  • 来到这里第一层就处理完毕了。接下来就是用 inorder = [9] 和 postorder = [9] 确定左子树; 用 inorder = [15,20,7] 和 postorder = [15,7,20] 来确定右子树。划分为两个子树之后,下面又是一样的递归流程了。

实现

public TreeNode buildTree(int[] inorder, int[] postorder) {
    return recursion(inorder, postorder, 0, inorder.length, 0, postorder.length);
}

private TreeNode recursion(int[] inorder, int[] postorder, int inStart, int inEnd, int postStart, int postEnd) {
    if (inStart >= inEnd || postStart >= postEnd) {
        return null;
    }
    TreeNode treeNode = new TreeNode(postorder[postEnd - 1]);
    // 找到划分中序遍历数组的点
    int inSplitIndex = -1;
    for (int i = inStart; i < inEnd; i++) {
        if (inorder[i] == postorder[postEnd - 1]) {
            inSplitIndex = i;
            break;
        }
    }
    // 找到划分后序遍历数组的点
    int postSplitIndex = postStart + (inSplitIndex - inStart);
    // 递归处理左子树
    treeNode.left = recursion(inorder, postorder, inStart, inSplitIndex, postStart, postSplitIndex);
    // 递归处理右子树
    treeNode.right = recursion(inorder, postorder, inSplitIndex + 1, inEnd, postSplitIndex , postEnd - 1);
    return treeNode;
}

在这里插入图片描述

【优化】

因为数组的元素是唯一的,可以使用字典来存储值和相应索引,避免每次需要循环寻找索引

public TreeNode buildTree1(int[] inorder, int[] postorder) {
    HashMap<Integer, Integer> valueAndIndexMap = new HashMap<>();
    for (int i = 0; i < inorder.length; i++) {
        valueAndIndexMap.put(inorder[i], i);
    }
    return recursion1(inorder, postorder, 0, inorder.length, 0, postorder.length, valueAndIndexMap);
}

private TreeNode recursion1(int[] inorder, int[] postorder, int inStart, int inEnd, int postStart, int postEnd, HashMap<Integer, Integer> valueAndIndexMap) {
    if (inStart >= inEnd || postStart >= postEnd) {
        return null;
    }
    TreeNode treeNode = new TreeNode(postorder[postEnd - 1]);
    // 找到划分中序遍历数组的点
    int inSplitIndex = valueAndIndexMap.get(postorder[postEnd - 1]);
    // 找到划分后序遍历数组的点
    int postSplitIndex = postStart + (inSplitIndex - inStart);
    // 递归处理左子树
    treeNode.left = recursion1(inorder, postorder, inStart, inSplitIndex, postStart, postSplitIndex, valueAndIndexMap);
    // 递归处理右子树
    treeNode.right = recursion1(inorder, postorder, inSplitIndex + 1, inEnd, postSplitIndex, postEnd - 1, valueAndIndexMap);
    return treeNode;
}

思考题

  • 知道后序遍历的数组和中序遍历的数组可以唯一确定一棵二叉树。
  • 知道前序遍历的数组和中序遍历的数组也可以唯一确定一棵二叉树。
  • 但**前序和后序不能唯一确定一棵二叉树,**因为没有中序遍历无法确定左右部分。

最大二叉树

https://leetcode.cn/problems/maximum-binary-tree/description/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

求解

public TreeNode constructMaximumBinaryTree(int[] nums) {
    return recursion(nums, 0, nums.length);
}

private TreeNode recursion(int[] nums, int start, int end) {
    if (end <= start) {
        return null;
    } else if (end == start + 1) {
        return new TreeNode(nums[start]);
    }
    int maxIndex = -1;
    // 由题目可知,数值的取值是[0,1000]
    int maxValue = -1;
    // 注意:循环的时候是左开右闭
    for (int i = start; i < end; i++) {
        if (nums[i] > maxValue) {
            maxValue = nums[i];
            maxIndex = i;
        }
    }
    TreeNode treeNode = new TreeNode(maxValue);
    // 因为是左开右闭,所以右边传入maxIndex
    treeNode.left = recursion(nums, start, maxIndex);
    treeNode.right = recursion(nums, maxIndex + 1, end);
    return treeNode;
}

在这里插入图片描述

合并二叉树

https://leetcode.cn/problems/merge-two-binary-trees/description/

在这里插入图片描述

递归法

public TreeNode mergeTrees(TreeNode node1, TreeNode node2) {
    if (node1 == null) {
        // --if-- node1为空,直接返回node2
        return node2;
    } else if (node2 == null) {
        return node1;
    } else {
        // --if-- 两个节点都不为空,将node2的值添加到node1上面
        node1.val += node2.val;
        // 处理左节点
        node1.left = mergeTrees(node1.left, node2.left);
        // 处理右节点
        node1.right = mergeTrees(node1.right, node2.right);
    }
    return node1;
}

在这里插入图片描述

迭代法 ※

public TreeNode mergeTrees(TreeNode node1, TreeNode node2) {
    if (node1 == null) {
        // --if-- node1为空,直接返回node2
        return node2;
    } else if (node2 == null) {
        return node1;
    }
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(node1);
    stack.push(node2);
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode pop2 = stack.pop();
        TreeNode pop1 = stack.pop();
        pop1.val += pop2.val;

        if (pop1.left != null && pop2.left != null) {
            stack.push(pop1.left);
            stack.push(pop2.left);
        } else if (pop1.left == null && pop2.left != null) {
            pop1.left = pop2.left;
        }

        if (pop1.right != null && pop2.right != null) {
            stack.push(pop1.right);
            stack.push(pop2.right);
        } else if (pop1.right == null && pop2.right != null) {
            pop1.right = pop2.right;
        }
    }
    return node1;
}

在这里插入图片描述

二叉搜索树中的搜索

https://leetcode.cn/problems/search-in-a-binary-search-tree/description/

在这里插入图片描述

在这里插入图片描述

递归法

public TreeNode searchBST(TreeNode node, int val) {
    if (node == null) {
        return null;
    }
    if (node.val == val) {
        return node;
    }
    // 递归看看左节点是否符合条件
    TreeNode left = searchBST(node.left, val);
    if (left != null) {
        // --if-- 左节点符合,直接返回,无需再往下遍历右节点
        return left;
    }
    return searchBST(node.right, val);
}

在这里插入图片描述

递归法(适配二叉搜索树)

【注意】该树是二叉搜索树

  • 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  • 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;

因此,只遍历左右子树中的一个即可

public TreeNode searchBST2(TreeNode node, int val) {
    if (node == null) {
        return null;
    }
    if (node.val == val) {
        return node;
    } else if (node.val > val) {
        return searchBST(node.left, val);
    }else {
        return searchBST(node.right, val);
    }
}

在这里插入图片描述

迭代法

public TreeNode searchBST1(TreeNode node, int val) {
    if (node == null) {
        return null;
    }
    Deque<TreeNode> queue = new ArrayDeque<>();
    queue.push(node);
    while (!queue.isEmpty()) {
        TreeNode poll = queue.poll();
        if (poll.val == val) {
            return poll;
        }
        if (poll.right != null) {
            queue.push(poll.right);
        }
        if (poll.left != null) {
            queue.push(poll.left);
        }
    }
    return null;
}

在这里插入图片描述

迭代法(适配二叉搜索树)

public TreeNode searchBST(TreeNode node, int val) {
    if (node == null) {
        return null;
    }
    Deque<TreeNode> queue = new ArrayDeque<>();
    queue.push(node);
    while (!queue.isEmpty()) {
        TreeNode poll = queue.poll();
        if (poll.val == val) {
            return poll;
        } else if (poll.val > val) {
            // --if-- 当前节点值大于val,往左子树搜索即可
            if (poll.left != null) queue.push(poll.left);
        } else {
            // --if-- 当前节点值大于val,往左子树搜索即可
            if (poll.right != null) queue.push(poll.right);
        }
    }
    return null;
}

在这里插入图片描述

验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/description/

在这里插入图片描述

在这里插入图片描述

递归法(前序遍历)

【基础实现】

  • 要判断一棵树是否为二叉搜索树,需要左子节点的最大值<当前节点的值,右子节点的最小值>当前节点值
  • 使用一个对象来存储一颗树的最大、最小值
class Value {
    /**
     * 当前节点对应子树的最大节点值
     */
    int max;
    /**
     * 当前节点对应子树的最小节点值
     */
    int min;
    /**
     * 当前节点对应二叉树是否满足二叉搜索树要求
     */
    boolean isReasonable;

    public Value(int max, int min, boolean isReasonable) {
        this.max = max;
        this.min = min;
        this.isReasonable = isReasonable;
    }
}

public boolean isValidBST(TreeNode root) {
    return recursion(root).isReasonable;
}

private Value recursion(TreeNode node) {
    if (node.left == null && node.right == null) {
        // --if-- 左右子节点都为空时,树的最大最小值都为当前节点的值
        return new Value(node.val, node.val, true);
    }
    Value leftValue = null;
    if (node.left != null) {
        leftValue = recursion(node.left);
        if (leftValue.isReasonable == false) {
            // --if-- 左子树不合法,直接返回
            return leftValue;
        }
//            System.out.println("当前节点值:" + node.val + " 左子树最大值:" + leftValue.max);
        if (leftValue.max >= node.val) {
            // --if-- 左子树的最大值大于等于当前节点的值,不合法,返回
            leftValue.isReasonable = false;
            return leftValue;
        }
    }
    Value rightValue = null;
    if (node.right != null) {
        rightValue = recursion(node.right);
        if (rightValue.isReasonable == false) {
            // --if-- 右子树不合法,直接返回
            return rightValue;
        }
//            System.out.println("当前节点值:" + node.val + " 右子树最小值:" + rightValue.min);
        if (rightValue.min <= node.val) {
            // --if-- 右子树的最小值小于等于当前节点的值,不合法,返回
            rightValue.isReasonable = false;
            return rightValue;
        }
    }

    return new Value(
            rightValue == null ? node.val : rightValue.max,
            leftValue == null ? node.val : leftValue.min,
            true);
}

多次提交性能不同

在这里插入图片描述

在这里插入图片描述

递归法(中序遍历)※

TreeNode max;
public boolean isValidBST(TreeNode node) {
    if (node == null) {
        return true;
    }
    // 左
    boolean left = isValidBST(node.left);
    if (!left) {
        return false;
    }
    // 中
    if (max != null && node.val <= max.val) {
        return false;
    }
    max = node;
    // 右
    return isValidBST(node.right);
}

如果二叉树为二叉搜索树的话,随着中序遍历的进行,max的值是一直递增的,如果node.val <= max.val,说明不满足二叉搜索树

在这里插入图片描述

迭代法(中序遍历)

和中序遍历迭代法代码差不多,获取元素的时候,比较一下大小就行

public boolean isValidBST(TreeNode root) {
    if (root == null) return false;
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode maxNode = null;
    TreeNode cur = root;
    stack.push(cur);
    while (cur != null && !stack.isEmpty()) {
        if (cur.left != null) {
            // --if-- 如果当前节点还有左节点,将其加入栈中
            stack.push(cur.left);
            cur = cur.left;
        } else {
            // 从栈中不断弹出元素,直到弹出的元素,右节点不为空
            while (!stack.isEmpty()) {
                TreeNode pop = stack.pop();
                if (maxNode != null && pop.val <= maxNode.val) {
                    return false;
                }
                maxNode = pop;
                if (pop.right != null) {
                    // --if-- 当前节点有右节点,开始遍历右节点
                    stack.push(pop.right);
                    cur = pop.right;
                    break;
                }
            }
        }
    }
    return true;
}

在这里插入图片描述

二叉搜索树的最小绝对差

https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/

在这里插入图片描述

在这里插入图片描述

递归法(中序遍历)※

如果是二叉搜索树,牢牢记住中序遍历出来的数值是从小到大的,只需要依次比较前后两个节点值的差是否更小即可

private TreeNode lastNode;
private int min = Integer.MAX_VALUE;
private int temp;

public int getMinimumDifference(TreeNode node) {
    if (node == null) {
        return 0;
    }
    // 遍历左子树
    getMinimumDifference(node.left);

    // 更新最小值
    if (lastNode != null) {
        temp = node.val - lastNode.val;
        if (temp < min) {
            min = temp;
        }
    }
    // 更新上一个所遍历的节点
    lastNode = node;
    
    // 遍历右子树
    getMinimumDifference(node.right);
    return min;
}

在这里插入图片描述

迭代法(中序遍历)

public int getMinimumDifference1(TreeNode node) {
    if (node == null) return 0;
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode lastNode = null;
    int minDiff = Integer.MAX_VALUE;
    int tempDiff;
    TreeNode cur = node;
    stack.push(cur);
    while (cur != null && !stack.isEmpty()) {
        if (cur.left != null) {
            // --if-- 如果当前节点还有左节点,将其加入栈中
            stack.push(cur.left);
            cur = cur.left;
        } else {
            // 从栈中不断弹出元素,直到弹出的元素,右节点不为空
            while (!stack.isEmpty()) {
                TreeNode pop = stack.pop();
                if (lastNode != null) {
                    tempDiff = pop.val - lastNode.val;
                    if (tempDiff < minDiff) {
                        minDiff = tempDiff;
                    }
                }
                lastNode = pop;
                if (pop.right != null) {
                    // --if-- 当前节点有右节点,开始遍历右节点
                    stack.push(pop.right);
                    cur = pop.right;
                    break;
                }
            }
        }
    }
    return minDiff;
}

在这里插入图片描述

二叉搜索树中的众数

https://leetcode.cn/problems/find-mode-in-binary-search-tree/description/

在这里插入图片描述

在这里插入图片描述

如果不是二叉搜索树,随便用哪种遍历,用map记录每个值的频数,然后对频数降序排序

递归法(中序遍历)

/**
 * 存储上一个节点
 */
private TreeNode lastNode;
/**
 * 存储最高频
 */
private int maxNum = 0;
/**
 * 记录当前数出现的频次
 */
private int temp = 0;

public int[] findMode(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    recursion(result, root);
    int[] resultArr = new int[result.size()];
    for (int i = 0; i < result.size(); i++) {
        resultArr[i] = result.get(i);
    }
    return resultArr;
}

public void recursion(List<Integer> result, TreeNode node) {
    if (node == null) {
        return;
    }
    // 遍历左子树
    recursion(result, node.left);

    // 更新最小值
    if (lastNode != null) {
        if (node.val != lastNode.val) {
            // --if-- 当前节点值和上一个节点值不同,temp计数改为1,即记录当前节点的频数
            temp = 1;
        } else {
            // --if-- 当前节点值和上一个节点值相同,temp计数++
            temp++;
        }
        if (temp > maxNum) {
            // --if-- 找到了更大频数的数
            // 清空原本的众数集合
            result.clear();
            // 将新的众数添加到结果集中
            result.add(lastNode.val);
            // 更新最新数量
            maxNum = temp;
        } else if (temp == maxNum) {
            // --if-- 当前数的频数和众数频数一样大,将其添加到众数结果集中
            result.add(node.val);
        }
    } else {
        // --if-- 中序遍历的第一个节点,添加到结果集中,temp和maxNum设置为1
        result.add(node.val);
        temp = 1;
        maxNum = 1;
    }

    // 更新上一个所遍历的节点
    lastNode = node;

    // 遍历右子树
    recursion(result, node.right);
}

在这里插入图片描述

迭代法(中序遍历)

public int[] findMode(TreeNode root) {
    List<Integer> resultList = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode lastNode = null;
    /**
     * 存储最高频
     */
     int maxNum = 0;
    /**
     * 记录当前数出现的频次
     */
     int temp = 0;
    TreeNode cur = root;
    stack.push(cur);
    while (cur != null && !stack.isEmpty()) {
        if (cur.left != null) {
            // --if-- 如果当前节点还有左节点,将其加入栈中
            stack.push(cur.left);
            cur = cur.left;
        } else {
            // 从栈中不断弹出元素,直到弹出的元素,右节点不为空
            while (!stack.isEmpty()) {
                TreeNode pop = stack.pop();
                // 更新最小值
                if (lastNode != null) {
                    if (pop.val != lastNode.val) {
                        // --if-- 当前节点值和上一个节点值不同,temp计数改为1,即记录当前节点的频数
                        temp = 1;
                    } else {
                        // --if-- 当前节点值和上一个节点值相同,temp计数++
                        temp++;
                    }
                    if (temp > maxNum) {
                        // --if-- 找到了更大频数的数
                        // 清空原本的众数集合
                        resultList.clear();
                        // 将新的众数添加到结果集中
                        resultList.add(lastNode.val);
                        // 更新最新数量
                        maxNum = temp;
                    } else if (temp == maxNum) {
                        // --if-- 当前数的频数和众数频数一样大,将其添加到众数结果集中
                        resultList.add(pop.val);
                    }
                } else {
                    // --if-- 中序遍历的第一个节点,添加到结果集中,temp和maxNum设置为1
                    resultList.add(pop.val);
                    temp = 1;
                    maxNum = 1;
                }
                lastNode = pop;
                if (pop.right != null) {
                    // --if-- 当前节点有右节点,开始遍历右节点
                    stack.push(pop.right);
                    cur = pop.right;
                    break;
                }
            }
        }
    }
    int[] resultArr = new int[resultList.size()];
    for (int i = 0; i < resultList.size(); i++) {
        resultArr[i] = resultList.get(i);
    }
    return resultArr;
}

在这里插入图片描述

二叉树的最近公共祖先

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/

在这里插入图片描述

在这里插入图片描述

递归法(后序遍历)※

private TreeNode fatherNode;

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    recursion(root, p, q);
    return fatherNode;
}

private TreeNode recursion(TreeNode node, TreeNode p, TreeNode q) {
    if (node == null) {
        return null;
    }
    TreeNode left = recursion(node.left, p, q);
    TreeNode right = recursion(node.right, p, q);
    if (left != null && right != null) {
        // --if-- 左右子树 都找到了p、q的一个,说明当前节点即为最近公开祖先
        fatherNode = node;
        return null;
    } else if ((left != null || right != null) && (node.val == p.val || node.val == q.val)) {
        // --if-- 左右子树 只找到了p、q的一个,且当前节点等于p、q的一个,说明当前节点即为最近公开祖先
        fatherNode = node;
        return null;
    }
    if (node.val == p.val || node.val == q.val) {
        // --if-- 当前节点为p、q中的一个,返回当前节点
        return node;
    } else if (left != null) {
        // --if-- 当前节点的左子树,返回了p、q中的一个,返回left
        return left;
    } else if (right != null) {
        // --if-- 当前节点的左子树,返回了p、q中的一个,返回right
        return right;
    }
    return null;
}

在这里插入图片描述

【代码优化】

public TreeNode lowestCommonAncestor1(TreeNode node, TreeNode p, TreeNode q) {
    if (node == null || node == p || node == q) { 
        // --if-- 如果当前节点就是 p 或 q ,直接返回当前节点,不需要管后面有没有 p 或 q 的另一个
        return node;
    }
    
    TreeNode left = lowestCommonAncestor1(node.left, p, q);
    TreeNode right = lowestCommonAncestor1(node.right, p, q);

    if (left != null && right != null) {
        // --if-- 在左、右子树种分别找到了节点 p、q,说明当前节点即为最近公开祖先
        return node;
    } else if (left != null) {
        // --if-- 当前节点的左子树,返回了p、q中的一个,返回left
        return left;
    } else if (right != null) {
        // --if-- 当前节点的左子树,返回了p、q中的一个,返回right
        return right;
    } else {
        // --if-- 未找到节点 p、q
        return null;
    }
}

在这里插入图片描述

迭代法(不适合求解该题,很麻烦)

二叉搜索树中的插入操作 ※

https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

递归法

public TreeNode insertIntoBST(TreeNode node, int val) {
    if (node == null) {
        return new TreeNode(val);
    }
    if (val > node.val) {
        // --if-- val比node.val更大,往右子树插入
        // 如果 node.right 不为空,会返回 node.right 本身
        node.right = insertIntoBST(node.right, val);
    } else if (val < node.val) {
        // --if-- val比node.val更小,往左子树插入
        node.left = insertIntoBST(node.left, val);
    }
    return node;
}

在这里插入图片描述

迭代法

public TreeNode insertIntoBST(TreeNode root, int val) {
    if (root == null) return new TreeNode(val);
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        // 每次循环从栈中取出一个节点存储到结果集中
        TreeNode pop = stack.pop();
        if (val > pop.val) {
            if (pop.right != null) {
                // 右子树不为空,继续往下遍历,将节点存储到栈中
                stack.push(pop.right);
            } else {
                // 右子树为空,直接插入新节点
                pop.right = new TreeNode(val);
            }
        } else if (val < pop.val) {
            if (pop.left != null) {
                stack.push(pop.left);
            } else {
                pop.left = new TreeNode(val);
            }
        }
    }
    return root;
}

在这里插入图片描述

删除二叉搜索树中的节点 ※

https://leetcode.cn/problems/delete-node-in-a-bst/description/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

递归法

public TreeNode deleteNode(TreeNode node, int key) {
    if (node == null) {
        return null;
    }
    if (node.val == key) {
        // --if-- 当前节点为要删除节点
        if (node.left == null && node.right == null) {
            // --if-- 当前节点没有子节点,直接删除该节点
            return null;
        } else if (node.left != null && node.right == null) {
            // --if-- 当前节点左节点不为空,把左节点放到当前节点位置
            return node.left;
        } else if (node.left == null && node.right != null) {
            // --if-- 当前节点右节点不为空,把右节点放到当前节点位置
            return node.right;
        } else {
            // --if-- 当前节点左、右节点不为空,把左子树挂载到右子树的最左节点,然后把右节点放到当前节点位置
            TreeNode cur = node.right;
            // 寻找右子树的最左节点
            while (cur.left != null) {
                cur = cur.left;
            }
            cur.left = node.left;
            return node.right;
        }
    } else if (key > node.val) {
        // --if-- key > 当前节点的值,往右子树寻找
        node.right = deleteNode(node.right, key);
    } else if (key < node.val) {
        // --if-- key < 当前节点的值,往右子树寻找
        node.left = deleteNode(node.left, key);
    }
    return node;
}

在这里插入图片描述

【代码简化】

不需要判断node.left != null && node.right == null,只要node.right == null时,就只传left即可,不需要管left是否为空,为空会直接返回,不影响算法结果

public TreeNode deleteNode(TreeNode node, int key) {
    if (node == null) {
        return null;
    }
    if (node.val == key) {
        // --if-- 当前节点为要删除节点
        if (node.left == null && node.right == null) {
            // --if-- 当前节点没有子节点,直接删除该节点
            return null;
        } else if (node.right == null) {
            // --if-- 当前节点左节点不为空,把左节点放到当前节点位置
            return node.left;
        } else if (node.left == null) {
            // --if-- 当前节点右节点不为空,把右节点放到当前节点位置
            return node.right;
        } else {
            // --if-- 当前节点左、右节点不为空,把左子树挂载到右子树的最左节点,然后把右节点放到当前节点位置
            TreeNode cur = node.right;
            // 寻找右子树的最左节点
            while (cur.left != null) {
                cur = cur.left;
            }
            cur.left = node.left;
            return node.right;
        }
    } else if (key > node.val) {
        // --if-- key > 当前节点的值,往右子树寻找
        node.right = deleteNode(node.right, key);
    } else if (key < node.val) {
        // --if-- key < 当前节点的值,往右子树寻找
        node.left = deleteNode(node.left, key);
    }
    return node;
}

迭代法

public TreeNode deleteNode(TreeNode root, int key) {
    // 存储下一个要遍历的节点
    TreeNode node = root;
    // 存储删除过的节点
    TreeNode nodeAfterDelete;
    // 存储上一个遍历到的节点
    TreeNode pre = null;
    // node是pre的哪个节点,0:左节点;1:右节点
    int preType = 0;
    while (node != null) {
        if (node.val == key) {
            // --if-- 当前节点为要删除节点
            if (node.left == null && node.right == null) {
                // --if-- 当前节点没有子节点,直接删除该节点
                nodeAfterDelete = null;
            } else if (node.left != null && node.right == null) {
                // --if-- 当前节点左节点不为空,把左节点放到当前节点位置
                nodeAfterDelete = node.left;
            } else if (node.left == null && node.right != null) {
                // --if-- 当前节点右节点不为空,把右节点放到当前节点位置
                nodeAfterDelete = node.right;
            } else {
                // --if-- 当前节点左、右节点不为空,把左子树挂载到右子树的最左节点,然后把右节点放到当前节点位置
                TreeNode cur = node.right;
                // 寻找右子树的最左节点
                while (cur.left != null) {
                    cur = cur.left;
                }
                cur.left = node.left;
                nodeAfterDelete = node.right;
            }
            if (pre == null) {
                // --if-- pre=null,说明删除的是根节点,直接返回即可,不用进行值的设置
                return nodeAfterDelete;
            } else {
                // 删除节点
                if (preType == 0) {
                    pre.left = nodeAfterDelete;
                } else {
                    pre.right = nodeAfterDelete;
                }
                // 删除结束了,直接返回root即可
                return root;
            }
        } else if (key > node.val && node.right != null) {
            // key > node.val且右子树不为空,继续往下遍历
            pre = node;
            node = node.right;
            preType = 1;
        } else if (key < node.val && node.left != null) {
            // key < node.val且左子树不为空,继续往下遍历
            pre = node;
            node = node.left;
            preType = 0;
        } else {
            // 结束循环
            node = null;
        }
    }
    return root;
}

修剪二叉搜索树 ※

https://leetcode.cn/problems/trim-a-binary-search-tree/

在这里插入图片描述

在这里插入图片描述

递归法

public TreeNode trimBST(TreeNode node, int low, int high) {
    if (node == null) {
        return null;
    }
    if (node.val < low || node.val > high) {
        // --if-- 当前节点为要删除节点
        if (node.left == null && node.right == null) {
            // --if-- 当前节点没有子节点,直接删除该节点
            return null;
        } else if (node.left != null && node.right == null) {
            // --if-- 当前节点左节点不为空,需要继续判断左子节点是否在[low , high]内部,因此继续递归调用trimBST
            return trimBST(node.left, low, high);
        } else if (node.left == null && node.right != null) {
            // --if-- 当前节点右节点不为空,需要继续判断右子节点是否在[low , high]内部,因此继续递归调用trimBST
            return trimBST(node.right, low, high);
        } else {
            // --if-- 当前节点左、右节点不为空
            if (node.val < low) {
                // --if-- 因为node.val < low,左边的节点肯定小于low,因此只用看右边,需要继续判断右子节点是否在[low , high]内部,因此继续递归调用trimBST
                return trimBST(node.right, low, high);
            } else {
                // --if-- 因为node.val > low,右边的节点肯定大于low,因此只用看左边,需要继续判断左子节点是否在[low , high]内部,因此继续递归调用trimBST
                return trimBST(node.left, low, high);
            }
        }
    } else {
        // 当前节点无需修剪,递归修剪左子树
        node.left = trimBST(node.left, low, high);
        // 当前节点无需修剪,递归修剪右子树
        node.right = trimBST(node.right, low, high);
    }
    return node;
}

在这里插入图片描述

【代码简化】

 public TreeNode trimBST(TreeNode node, int low, int high) {
    if (node == null) {
        return null;
    }
    if (node.val < low) {
        // --if-- 因为node.val < low,左边的节点肯定小于low,因此只用看右边,需要继续判断右子节点是否在[low , high]内部,因此继续递归调用trimBST
        return trimBST(node.right, low, high);
    }
    if (node.val > high) {
        // --if-- 因为node.val > low,右边的节点肯定大于low,因此只用看左边,需要继续判断左子节点是否在[low , high]内部,因此继续递归调用trimBST
        return trimBST(node.left, low, high);
    }
    // root在[low,high]范围内
    node.left = trimBST(node.left, low, high);
    node.right = trimBST(node.right, low, high);
    return node;
}

迭代法

public TreeNode trimBST(TreeNode root, int low, int high) {
    if (root == null) {
        return null;
    }
    // 连续修剪,先把前面能删的删了
    while (root != null) {
        if (root.val < low) {
            root = root.right;
        } else if (root.val > high) {
            root = root.left;
        } else {
            break;
        }
    }
    /// 处理左子树
    TreeNode cur = root;
    while (cur != null) {
        // 为什么只会判断是否小于 low 呢?
        // 原因: cur 在 [low,high] 之间, cur 的左子树的节点的值是不可能 >high 的,所以无需判断
        while (cur.left != null && cur.left.val < low) {
            cur.left = cur.left.right;
        }
        cur = cur.left;
    }

    /// 处理左子树
    // 指针重新回到根节点
    cur = root;
    while (cur != null) {
        // 为什么只会判断是否大于 high 呢?
        // 原因和上面的类似
        while (cur.right != null && cur.right.val > high) {
            cur.right = cur.right.left;
        }
        cur = cur.right;
    }

    return root;
}

在这里插入图片描述

在这里插入图片描述

将有序数组转换为二叉搜索树※

https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

思路

不断取数组中间值作为父节点,数组中间值左半边数组获取左子节点,右半边数组获取右子节点

递归法

public TreeNode sortedArrayToBST(int[] nums) {
    // 注意,调用方式为左闭右开
    return recursion(nums, 0, nums.length);
}

private TreeNode recursion(int[] nums, int start, int end) {
    // 因为是左闭右开,所以start=end是没有意义的
    if (start >= end) {
        return null;
    }
    // 获取中间的索引
    int center = (start + end) / 2;
    TreeNode treeNode = new TreeNode(nums[center]);
    // 注意,调用方式为左闭右开,所以不需要center-1
    treeNode.left = recursion(nums, start, center);
    treeNode.right = recursion(nums, center + 1, end);
    return treeNode;
}

在这里插入图片描述

注意:int center = (start + end) / 2;有一个问题,如果start和end都比较大,容易出现int越界,建议这样写int center = start + ((end - start) / 2);

迭代法

public TreeNode sortedArrayToBST(int[] nums) {
    // 存储node
    Deque<TreeNode> nodeStack = new ArrayDeque<>();
    // 存储索引
    Deque<Integer> indexStack = new ArrayDeque<>();
    // 生成根节点
    TreeNode treeNode = generateNode(nums, 0, nums.length, nodeStack, indexStack);

    while (!nodeStack.isEmpty()) {
        TreeNode pop = nodeStack.pop();
        Integer end = indexStack.pop();
        Integer center = indexStack.pop();
        Integer start = indexStack.pop();
        // 这里和递归是一样的思路
        pop.left = generateNode(nums, start, center, nodeStack, indexStack);
        pop.right = generateNode(nums, center + 1, end, nodeStack, indexStack);
    }

    return treeNode;
}

private TreeNode generateNode(int[] nums, int start, int end, Deque<TreeNode> nodeStack, Deque<Integer> indexStack) {
    if (start >= end) {
        return null;
    }
    // 获取中间的索引
    int center = (start + end) / 2;
    TreeNode treeNode = new TreeNode(nums[center]);
    nodeStack.add(treeNode);
    indexStack.add(end);
    indexStack.add(center);
    indexStack.add(start);
    return treeNode;
}

在这里插入图片描述

把二叉搜索树转换为累加树※

https://leetcode.cn/problems/convert-bst-to-greater-tree/description/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

递归法

【思路】

该题一看就是使用中序遍历,但是顺序不是左-中-右,而是右-中-左

要注意的是,计算当前节点的累加值,需要使用右子树的最大值。但是在累加树中,当前节点的右子节点值为21,因此还需要想办法存储右子树的最大值,用什么来存比较好呢?为了不增加额外的内容,可以使用原树的右子节点的val来存储(如下图原树节点中的红字),因为那个val已经没有其他作用了

在这里插入图片描述

public TreeNode convertBST(TreeNode root) {
    return recursion(root, 0);
}

private TreeNode recursion(TreeNode node, int max) {
    if (node == null) {
        return null;
    }
    // 先把自身节点的值放进去
    TreeNode treeNode = new TreeNode(node.val);
    // 遍历右子树
    treeNode.right = recursion(node.right, max);
    // 如果右子树不为空,当前节点的值再加上原树中右子树的最大值
    treeNode.val += treeNode.right != null ? node.right.val : max;
    // 遍历左子树
    treeNode.left = recursion(node.left, treeNode.val);
    
    /// 更新原树中当前节点对应树的最大值
    if (treeNode.left != null) {
        // --if-- 左子树不为null,当前树的最大值更新为左子树的最大值
        node.val = node.left.val;
    } else {
        // --if-- 左子树为null,当前树的最大值即为treeNode的值
        node.val = treeNode.val;
    }
    return treeNode;
}

在这里插入图片描述

从执行结果可以看到,内存使用的比较多,原因是上面的做法重新创建了一个树,为了优化,可以用原来的树来改

private int lastMax;

public TreeNode convertBST(TreeNode root) {
    lastMax = 0;
    recursion(root);
    return root;
}

private void recursion(TreeNode node) {
    if (node == null) {
        return;
    }
    // 遍历右子树
    recursion(node.right);
    // 当前节点值累加 遍历完右子树的最大值
    node.val += lastMax;
    // 更新最大值
    lastMax = node.val;
    // 遍历左子树
    recursion(node.left);
}

在这里插入图片描述

迭代法

拿中序遍历的代码来稍微修改一下即可

public TreeNode convertBST(TreeNode root) {
    if (root == null) return null;
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode cur = root;
    stack.push(cur);
    int lastMax = 0;
    while (cur != null && !stack.isEmpty()) {
        if (cur.right != null) {
            // --if-- 如果当前节点还有右节点,将其加入栈中
            stack.push(cur.right);
            cur = cur.right;
        } else {
            // 从栈中不断弹出元素,直到弹出的元素,左节点不为空
            while (!stack.isEmpty()) {
                TreeNode pop = stack.pop();
                pop.val += lastMax;
                lastMax = pop.val;
                if (pop.left != null) {
                    // --if-- 当前节点有左节点,开始遍历左节点
                    stack.push(pop.left);
                    cur = pop.left;
                    break;
                }
            }
        }
    }
    return root;
}

在这里插入图片描述

举报

相关推荐

0 条评论