算法总结——二叉树【hot100】(上)

题目1------二叉树中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

示例 1:

复制代码
输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

复制代码
输入:root = []
输出:[]

示例 3:

复制代码
输入:root = [1]
输出:[1]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

方法肯定是两个递归与迭代

二叉树的中序遍历:按照访问左子树------根节点------右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。

递归遍历:

java 复制代码
// 递归实现
public static void inorder(TreeNode root, List<Integer> res){
    if (root == null) {
        return;
    }
    inorder(root.left, res);
    res.add(root.val);
    inorder(root.right, res);
}

public static List<Integer> inorderTraversal(TreeNode root){
    /*
        * 递归中序遍历,使用辅助传参数
        * */
    List<Integer> res = new ArrayList<>();
    inorder(root, res);
    return res;
}

迭代遍历:

使用栈实现中序遍历

java 复制代码
public static List<Integer> inorderTraversal02(TreeNode root){
        /*
         * 使用双向队列栈结构,实现迭代中序遍历
         * */
        if (root == null){
            return new ArrayList<>();
        }
        List<Integer> res = 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();
            res.add(cur.val);
            cur = cur.right;
        }
        return res;
    }

题目2------二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

示例 1:

复制代码
输入:root = [3,9,20,null,null,15,7]
输出:3

示例 2:

复制代码
输入:root = [1,null,2]
输出:2

提示:

  • 树中节点的数量在 [0, 104] 区间内。
  • -100 <= Node.val <= 100

思路:遍历的时候往下走一次就会深度加1,在遍历的基础上维护一个深度。深度优先搜索DFS

递归思路非常快捷:

java 复制代码
public static int maxDepth(TreeNode root) {
        /*
        * 递归方法实现,最大深度Math.max(左子树最大深度,右子树最大深度)+1
        * */
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }

迭代思路:使用队列进行,但是要注意要一层一层的处理,而不是单纯的非叶子节点

java 复制代码
public static int maxDepth(TreeNode root) {
    /*
         * 迭代方法,使用队列逐层遍历,广度优先搜索
         * */
    if (root == null) {
        return 0;
    }
    Queue<TreeNode> queue = new ArrayDeque<>();
    int ans = 0;
    queue.add(root);
    while (!queue.isEmpty()) {
        // 要弹出一层,塞进下一层
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            // 弹出一层进一层
            TreeNode node = queue.poll();
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        ans += 1;
    }

    return ans;
}

题目3------翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

复制代码
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:

复制代码
输入:root = [2,1,3]
输出:[2,3,1]

示例 3:

复制代码
输入:root = []
输出:[]

提示:

  • 树中节点数目范围在 [0, 100]
  • -100 <= Node.val <= 100

思路:递归翻转,自底向上

java 复制代码
 public TreeNode invertTree(TreeNode root) {
        /*
        * 递归,自底向上翻转
        * */
        if (root == null){
            return null;
        }
        if (root.left == null && root.right == null) {
            return root;
        }
        TreeNode cur = root;
        TreeNode left = invertTree(cur.left);
        TreeNode right = invertTree(cur.right);
        cur.right = left;
        cur.left = right;
        return cur;
    }

迭代:层序遍历交换?

java 复制代码
public TreeNode invertTree(TreeNode root) {
        /*
         * 递归,自底向上翻转
         * */
        if (root == null){
            return null;
        }
        if (root.left == null && root.right == null) {
            return root;
        }
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            TreeNode temp = cur.left;
            cur.left = cur.right;
            cur.right = temp;

            if (cur.right != null) {
                queue.offer(cur.right);
            }
            if (cur.left != null) {
                queue.offer(cur.left);
            }

        }
        return root;
    }

题目4------对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

复制代码
输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

复制代码
输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000]
  • -100 <= Node.val <= 100

**进阶:**你可以运用递归和迭代两种方法解决这个问题吗?

思路:

核心思路:把一棵树当成"两棵树"来比较

要判断一棵树是否对称,我们实际上是在判断它的左子树右子树是否是相互翻转的(即互为镜像)。

这需要我们写一个辅助函数,同时传入两个节点(左子节点和右子节点),让它们"相向而行"进行比较:

  1. 左树的 孩子,要和右树的孩子比较(外侧边缘)。
  2. 左树的 孩子,要和右树的孩子比较(内侧边缘)。

递归:

递归方法就是需要弄清楚临界条件,核心逻辑就是在u==v的前提下展开,眼看u,v为不为空的具体情况

java 复制代码
// 递归方法:
public boolean isSymmetric(TreeNode root){
    /*
        * 翻转树然后比较.按层比较,在翻转的过程中比较,一个正向遍历一个反向遍历比较
        * */
    if (root == null) {
        return true;
    }
    return check(root, root);

}

public static boolean check(TreeNode u, TreeNode v) {
    /*
        * 递归检查,一个正向遍历一个反向遍历
        * */
    // 临界条件
    if (u == null && v == null) {
        return true;
    }
    if (u == null || v == null) {
        return false;
    }
    if (u.val != v.val) {
        return false;
    }

    // 核心逻辑
    boolean left = check(u.left, v.right);
    boolean right = check(u.right, v.left);
    return left && right;
}

迭代:

使用LinkedList实现的队列

LinkedList可以存空值,但是ArrayDeque存空值就会报错。

实现类 底层结构 能否存 null 适用场景
ArrayDeque 循环数组 不能 (报错) 99% 的常规队列/栈需求(速度快、省内存)。
LinkedList 双向链表 你的算法逻辑必须依赖把 null 作为占位符放进队列里时。
java 复制代码
// 使用可以存null的集合LinkedList进行层序遍历
public boolean isSymmetric(TreeNode root){
        /*
        * 翻转树然后比较.按层比较,在翻转的过程中比较,一个正向遍历一个反向遍历比较
        * */
        if (root == null) {
            return true;
        }
        return check(root, root);

    }

    public static boolean check(TreeNode u, TreeNode v) {
        /*
         * 迭代检查,一个正向遍历一个反向遍历,层序遍历,一个队列即可
         * */
        Queue<TreeNode> q = new LinkedList<>();
        // 一个队列同时正反向遍历两棵树
        q.offer(u);
        q.offer(v);
        while (!q.isEmpty()) {
            u = q.poll();
            v = q.poll();
            // 空的情况
            if (u==null&&v==null){
                continue;
            }
            // 不对称的情况
            if((u==null||v==null)|| (u.val != v.val)){
                return false;
            }
            // 外侧入队,不用判断空值,因为
            q.offer(u.left);
            q.offer(v.right);
            // 内侧入队
            q.offer(u.right);
            q.offer(v.left);

        }
        return true;
    }

题目5------二叉树的直径

和最大路径和是同样的解法

给你一棵二叉树的根节点,返回该树的 直径

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root

两节点之间路径的 长度 由它们之间边数表示。(边数不是点数了)

示例 1:

复制代码
输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

示例 2:

复制代码
输入:root = [1,2]
输出:1

提示:

  • 树中节点数目在范围 [1, 104]
  • -100 <= Node.val <= 100

思路:基于递归求树高度的方法,最大值维护为左子树高度+右子树高度最大值

tips:最大直径不一定经过根,结果应该所有节点中的(左子树高度+右子树高度)最大值+2

可以将二叉树的直径转换为:二叉树的【每个节点的左右子树的高度和】的最大值

使用递归方法

java 复制代码
public static int ans = 0;

public static int diameterOfBinaryTree(TreeNode root){
    ans = 0;
    maxdepth(root);
    return ans;
}



public static int maxdepth(TreeNode node){
    /*
        * 计算一个节点的最大高度,并且在计算过程中,维护,最大的左右子树高度和
        * */
    if (node==null) {
        return 0;
    }
    int leftdepth = maxdepth(node.left);
    int rightdepth = maxdepth(node.right);

    int depthSum = leftdepth + rightdepth;
    // 维护一个全局变量记录过程中得到左右子树最大高度和
    ans = Math.max(ans, depthSum);

    return Math.max(leftdepth, rightdepth) + 1;
}

再好好品味下面的困难

题目6------二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和

示例 1:

复制代码
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2:

复制代码
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:

  • 树中节点数目范围是 [1, 3 * 104]
  • -1000 <= Node.val <= 1000

思路和求直径是相同的,只是这次就不是最高度的过程中去维护直径,而是求节点单边最大路径和的情况下,最去维护一个最大拱桥的最大路径和

题目7------二叉树的层序遍历【高频】

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

复制代码
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

复制代码
输入:root = [1]
输出:[[1]]

示例 3:

复制代码
输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

思路就是使用队列来进行层序遍历

进一层出一层,直到队列为空

java 复制代码
// 这次使用ArrayDeque
public static List<List<Integer>> levelOrder(TreeNode root){
        /*
        * 使用队列进行层序遍历
        * */
        if (root == null) {
            return new ArrayList<>();
        }
        Queue<TreeNode> queue = new ArrayDeque<>();
        List<List<Integer>> ans = new ArrayList<>();
        // 1.根节点入队
        queue.offer(root);
        while (!queue.isEmpty()){
            int size = queue.size();
            // 记录每一层结果
            List<Integer> layer = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                layer.add(node.val);
                // 放入这个节点的左右子节点

                if (node.left!=null) queue.offer(node.left);
                if (node.right!=null) queue.offer(node.right);
            }
            ans.add(layer);
        }
        return ans;
    }
相关推荐
百锦再1 小时前
Java中的char、String、StringBuilder与StringBuffer 深度详解
java·开发语言·python·struts·kafka·tomcat·maven
weixin_477271692 小时前
根象:树根。基石。基于马王堆帛书《周易》原文及甲骨文还原周朝生活活动现象(《函谷门》原创)
算法·图搜索算法
普通网友2 小时前
多协议网络库设计
开发语言·c++·算法
努力努力再努力wz2 小时前
【Linux网络系列】:TCP 的秩序与策略:揭秘传输层如何从不可靠的网络中构建绝对可靠的通信信道
java·linux·开发语言·数据结构·c++·python·算法
daxi1503 小时前
C语言从入门到进阶——第9讲:函数递归
c语言·开发语言·c++·算法·蓝桥杯
勇气要爆发3 小时前
LangGraph 实战:10分钟打造带“人工审批”的智能体流水线 (Python + LangChain)
开发语言·python·langchain
yy.y--3 小时前
Java数组逆序读写文件实战
java·开发语言
持续学习的程序员+14 小时前
强化学习Q-chunking算法
算法
Polaris北4 小时前
第二十七天打卡
开发语言·c++·算法