Leetcode百题斩-二叉树

news2025/6/2 2:53:47

二叉树作为经典面试系列,那么当然要来看看。总计14道题,包含大量的简单题,说明这确实是个比较基础的专题。快速过快速过。

先构造一个二叉树数据结构。


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

    public static TreeNode build(List<Integer> nodes) {
        if (nodes == null || nodes.isEmpty()) {
            return null;
        }
        List<TreeNode> treeList = new ArrayList<>();
        int nodePos = 0;
        int nodeLen = nodes.size();
        TreeNode root = new TreeNode(nodes.get(nodePos++));
        treeList.add(root);
        int treePos = 0;
        int treeLen = treeList.size();
        while (treePos < treeLen && nodePos < nodeLen) {
            TreeNode nowNode = treeList.get(treePos++);
            Integer nowVal = nodes.get(nodePos++);
            if (nowVal != null) {
                TreeNode leftNode = new TreeNode(nowVal);
                treeList.add(leftNode);
                nowNode.left = leftNode;
            }
            if (nodePos < nodeLen) {
                nowVal = nodes.get(nodePos++);
                if (nowVal != null) {
                    TreeNode rightNode = new TreeNode(nowVal);
                    treeList.add(rightNode);
                    nowNode.right = rightNode;
                }
            }
            treeLen = treeList.size();
        }
        return root;
    }

    private List<Integer> levelOrderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        List<TreeNode> treeQueue = new ArrayList<>();
        if (root == null) {
            return Collections.emptyList();
        }
        treeQueue.add(root);
        int len = treeQueue.size();
        int pos = 0;
        int actualLen = len;
        while (pos < len) {
            for (int i = pos; i < len; i++) {
                TreeNode treeNode = treeQueue.get(i);
                if (treeNode == null) {
                    if (i < actualLen) {
                        result.add(null);
                    }
                    continue;
                }
                result.add(treeNode.val);
                treeQueue.add(treeNode.left);
                if (treeNode.left != null) {
                    actualLen = treeQueue.size();
                }
                treeQueue.add(treeNode.right);
                if (treeNode.right != null) {
                    actualLen = treeQueue.size();
                }
                len = treeQueue.size();
            }
            pos = len;
        }
        return result;
    }

    @Override
    public String toString() {
        return levelOrderTraversal(this).toString();
    }
}

94. Binary Tree Inorder Traversal[Easy]

思路:中序遍历,没啥说的了,直接过

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
         if (root == null) {
            return new ArrayList<>();
        }
        List<Integer> result = new ArrayList<>();
        result.addAll(inorderTraversal(root.left));
        result.add(root.val);
        result.addAll(inorderTraversal(root.right));
        return result;
    }
}

98. Validate Binary Search Tree[Medium]

思路:验证二叉搜索树, 完全可以用上面编号102题层序遍历上面编号94题中序遍历的代码,最后判断一下中序遍历的结果是否递增即可

class Solution {

    public boolean isValidBST(TreeNode root) {
          List<Integer> inorderTraversal = inorderTraversal(root);
        int len = inorderTraversal.size();
        for (int i = 1; i < len; i++) {
            if (inorderTraversal.get(i) <= inorderTraversal.get(i - 1)) {
                return false;
            }
        }
        return true;
    }
}

当然,用前一题的思路中序遍历一下,然后再用一个变量来记录当前遍历的数值,可以完成优化

class Solution {

    private Integer current;

    public boolean isValidBST(TreeNode root) {
        if (root.left != null && !isValidBST(root.left)) {
            return false;
        }
        if (current != null && current >= root.val) {
            return false;
        }
        current = root.val;
        if (root.right != null && !isValidBST(root.right)) {
            return false;
        }
        return true;
    }
}

101. Symmetric Tree[Easy]

思路:判断二叉树是否对称。最近写回溯写多了,不过这不是个回溯,这就是个正向递归,递归左右子树判断是否对称即可

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isSymmetric(root.left, root.right);
    }

    public boolean isSymmetric(TreeNode leftTree, TreeNode rightTree) {
        if (leftTree == null && rightTree == null) {
            return true;
        }
        if (!(leftTree != null && rightTree != null)) {
            return false;
        }
        if (leftTree.val != rightTree.val) {
            return false;
        }
        if (!isSymmetric(leftTree.left, rightTree.right)) {
            return false;
        }
        if (!isSymmetric(leftTree.right, rightTree.left)) {
            return false;
        }
        return true;
    }
}

102. Binary Tree Level Order Traversal[Medium]

思路:二叉树层序遍历,没啥好说的,直接模拟队列即可

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
         List<TreeNode> treeQueue = new ArrayList<>();
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        treeQueue.add(root);
        int len = treeQueue.size();
        int pos = 0;
        while (pos < len) {
            List<Integer> levelResult = new ArrayList<>();
            for (int i = pos; i < len; i++) {
                TreeNode treeNode = treeQueue.get(i);
                levelResult.add(treeNode.val);
                if (treeNode.left != null) {
                    treeQueue.add(treeNode.left);
                }
                if (treeNode.right != null) {
                    treeQueue.add(treeNode.right);
                }
            }
            pos = len;
            len = treeQueue.size();
            result.add(levelResult);
        }
        return result;
    }
}

104. Maximum Depth of Binary Tree[Easy]

思路:求树的最大深度,这就是递归求左右子树最大深度然后对比即可,真一行代码搞定

class Solution {
    public int maxDepth(TreeNode root) {
        return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

105. Construct Binary Tree from Preorder and Inorder Traversal[Medium]

思路:根据前序遍历和中序遍历构造二叉树,这又是一道再经典不过的题了。前序遍历用于寻找父节点,推动遍历子树;中序遍历则用于划分左右子树。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
      Map<Integer, Integer> inorderMap = new HashMap<>();
        int len = inorder.length;
        for (int i = 0; i < len; i++) {
            inorderMap.put(inorder[i], i);
        }
        return buildTree(preorder, inorder, 0, len, 0, len, inorderMap);
    }

    private TreeNode buildTree(int[] preorder, int[] inorder, int preStartPos, int preEndPos, int inStartPos,
                                      int inEndPos, Map<Integer, Integer> inorderMap) {
        int root = preorder[preStartPos];
        if (preStartPos == preEndPos) {
            return new TreeNode(root);
        }
        int inRootPos = inorderMap.get(root);
        int leftLen = inRootPos - inStartPos;
        TreeNode leftTree = buildTree(preorder, inorder, preStartPos + 1, preStartPos + leftLen, inStartPos, inRootPos,
            inorderMap);
        TreeNode rightTree = buildTree(preorder, inorder, preStartPos + leftLen + 1, preEndPos, inRootPos + 1, inEndPos,
            inorderMap);
        return new TreeNode(root, leftTree, rightTree);
    }
}

108. Convert Sorted Array to Binary Search Tree[Easy]

思路:别看easy题就放飞自我了。将有序数组转换为二叉搜索树,不仅保证树平衡,也要保证每个子树平衡,因此也要用到递归的

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return sortedArrayToBST(nums, 0, nums.length);
    }

    private TreeNode sortedArrayToBST(int[] nums, int begin, int end) {
        if (begin >= end) {
            return null;
        }
        int mid = (begin + end) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        TreeNode leftTree = sortedArrayToBST(nums, begin, mid);
        TreeNode rightTree = sortedArrayToBST(nums, mid + 1, end);
        root.left = leftTree;
        root.right = rightTree;
        return root;
    }
}

114. Flatten Binary Tree to Linked List[Medium]

思路:将二叉树压成链。这就涉及到二叉树断链和合链操作了。想清楚写个递归即可

class Solution {
    public void flatten(TreeNode root) {
     flattenTree(root);
    }

    private TreeNode flattenTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        TreeNode leftTree = flattenTree(root.left);
        TreeNode rightTree = flattenTree(root.right);
        if (leftTree != null) {
            root.right = leftTree;
            root.left = null;
            if (rightTree != null) {
                while (leftTree.right != null) {
                    leftTree = leftTree.right;
                }
                leftTree.right = rightTree;
            }
        }
        return root;
    }
}

199. Binary Tree Right Side View[Medium]

思路:求二叉树的右视图,这个就完全可以用上面编号102题层序遍历的代码,然后取每层最后一个即可

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<List<Integer>> levelOrder = levelOrder(root);
        return levelOrder.stream().map(list -> list.get(list.size() - 1)).collect(Collectors.toList());
    }
}

当然,也可以将层序遍历的代码拿来优化一下,不需要记录层序遍历多余的节点,只需要记录每层末节点即可

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
         List<TreeNode> treeQueue = new ArrayList<>();
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return Collections.emptyList();
        }
        treeQueue.add(root);
        int len = treeQueue.size();
        int pos = 0;
        while (pos < len) {
            int lastNode = root.val;
            for (int i = pos; i < len; i++) {
                TreeNode treeNode = treeQueue.get(i);
                lastNode = treeNode.val;
                if (treeNode.left != null) {
                    treeQueue.add(treeNode.left);
                }
                if (treeNode.right != null) {
                    treeQueue.add(treeNode.right);
                }
            }
            pos = len;
            len = treeQueue.size();
            result.add(lastNode);
        }
        return result;
    }
}

230. Kth Smallest Element in a BST[Medium]

思路:求二叉搜索树的第k小值,又可以用到上面编号94题中序遍历的代码,然后取遍历数组中的第k个即可

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        List<Integer> inorderTraversal = inorderTraversal(root);
        return inorderTraversal.get(k - 1);
    }
}

当然,与上一题同样的原理,优化一下,用一个变量来记录一下当前遍历到的位置,这样就可以在搜索到结果的时候提前结束,降低时耗

class Solution {
    private int now = 1;

    public int kthSmallest(TreeNode root, int k) {
        if (root == null) {
            return -1;
        }
        int leftValue = kthSmallest(root.left, k);
        if (leftValue != -1) {
            return leftValue;
        }
        if (now == k) {
            return root.val;
        }
        now++;
        return kthSmallest(root.right, k);
    }
}

437. Path Sum III[Medium]

思路:求二叉树中路径和为特定树的路径个数。这一题有一点点树上DP的感觉,每个节点分别往左右子树上递归,分为计算该节点和不计算该结点进行转移。最后注意一个小细节,就是要注意边界问题,多个数的和是很可能超int边界的,注意要开成long

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
     return pathSum(root, targetSum, true) + pathSum(root, targetSum, false);
    }

    private int pathSum(TreeNode root, long targetSum, boolean include) {
        int count = 0;
        if (root == null) {
            return count;
        }
        if (include) {
            if (root.val == targetSum) {
                count++;
            }
            count += pathSum(root.left, targetSum - root.val, true);
            count += pathSum(root.right, targetSum - root.val, true);
        } else {
            count += pathSum(root.left, targetSum, true);
            count += pathSum(root.right, targetSum, true);
            count += pathSum(root.left, targetSum, false);
            count += pathSum(root.right, targetSum, false);
        }
        return count;
    }
}

求和问题,当然再来一套前缀和。经典的字符串前缀和可以看我之前的哈希系列

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
     HashMap<Long, Integer> sumMap = new HashMap<>();
        sumMap.put(0L, 1);
        return preSumPre(root, targetSum, sumMap, 0);
    }

    private int preSumPre(TreeNode root, int targetSum, Map<Long, Integer> sumMap, long currentSum) {
        int count = 0;
        if (root == null) {
            return count;
        }
        currentSum += root.val;
        if (sumMap.containsKey(currentSum - targetSum)) {
            count += sumMap.get(currentSum - targetSum);
        }
        sumMap.put(currentSum, sumMap.getOrDefault(currentSum, 0) + 1);
        count += preSumPre(root.left, targetSum, sumMap, currentSum);
        count += preSumPre(root.right, targetSum, sumMap, currentSum);
        sumMap.put(currentSum, sumMap.getOrDefault(currentSum, 0) - 1);
        return count;
    }
}

543. Diameter of Binary Tree[Easy]

思路:求二叉树最长路径。首先要递归求每个子树的最长路径,即等于左右子树的高度和。递归函数将子树高度返回,用一个单独的变量记录当前最长路径并在每个子树内比较更新即可

class Solution {
    private int maxLength = 0;

    public int diameterOfBinaryTree(TreeNode root) {
        getLength(root);
        return maxLength;
    }

    private int getLength(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftLength = getLength(root.left);
        int rightLength = getLength(root.right);
        if (leftLength + rightLength > maxLength) {
            maxLength = leftLength + rightLength;
        }
        return Math.max(leftLength, rightLength) + 1;
    }
}

236. Lowest Common Ancestor of a Binary Tree[Medium]

这就是前两天刚做过的通义app面试题,关于LCA的问题我专门特地整理了一下,感兴趣可以前去看看:

最近公共祖先(LCA)

226. Invert Binary Tree[Easy]

偶然发现,这题我在五年前校招刷题的时候也写过,当时的链接:二叉树翻转链接

左右子树递归交换一下即可

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {return null;}
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        root.left = right;
        root.right = left;
        return root;
    }
}

再来看一下当时我这题,我把他和另两道链表的题总结在了一起,刚好这个二叉树专题就要结束了,顺便预告温习一下百题斩的下一个专题,就是链表专题

最后,完结撒花,下个专题再见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ACM Latex模板:合并添加作者和单位

目录&#xff1a; 1.ACM会议论文Latex模板&#xff0c;逐个添加作者和单位&#xff1a; 1&#xff09;Latex&#xff1a; 2&#xff09;效果&#xff1a; 2. ACM会议论文Latex模板&#xff0c;合并添加作者和单位&#xff1a; 1&#xff09;Latex&#xff1a; 2&#x…

将MCP(ModelContextProtocol)与Semantic Kernel集成(调用github)

文章目录 将MCP&#xff08;ModelContextProtocol&#xff09;与Semantic Kernel集成&#xff08;调用github&#xff09;一、模型上下文协议&#xff08;MCP&#xff09;简介1.1 简介1.2 示例 二、集成步骤2.1 安装环境依赖2.2 构建语义内核&#xff08;Kernel&#xff09;2.3…

游戏引擎学习第311天:支持手动排序

仓库: https://gitee.com/mrxiao_com/2d_game_7(已满) 新仓库: https://gitee.com/mrxiao_com/2d_game_8 回顾并为今天的内容定下基调 我们接下来要继续完成之前开始的工作&#xff0c;上周五开始的部分内容&#xff0c;虽然当时对最终效果还不太确定&#xff0c;但现在主要任…

LambdaQueryWrapper、MybatisPlus提供的基本接口方法、增删改查常用的接口方法、自定义 SQL

DAY26.2 Java核心基础 MybatisPlus提供的基本接口方法 分页查询 导入依赖springboot整合Mybatis-plus <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version&g…

深度学习---可视化

模型可视化 深度学习模型可视化是理解、调试和优化模型的关键技术&#xff0c;涉及模型结构、参数、层输出、数据流动、训练过程等多维度分析。 一、可视化的核心作用 模型理解 解析复杂模型的网络架构&#xff08;如CNN的层级连接、Transformer的注意力机制&#xff09;。揭…

Windows系统如何查看ssh公钥

很多人只是一味的为拿到ssh公钥而努力&#xff0c;往往却会忽略了ssh公钥与私钥背后的作用。 咱们在这里会花两分钟。 一分钟速通概念&#xff0c;一分钟教会你如何获取。 一分钟速通概念&#xff1a; 如何生成&#xff1a; SHH 公钥 与 私钥 是基于非对称加密算法&#xff…

【2025】嵌入式软考中级部分试题

大题: 大模型 神经网络 机器学习 深度学习的包含关系 不一定对 订阅-发布者模型 发布/订阅模式特点: ①解耦:发布者和订阅者之间没有直接联系,它们通过中间的消息代理(如消息队列或事件总线)进行通信。这种解耦使得系统更加灵活,可以独立地添加或移除发布者和订阅者…

Antd中Upload组件封装及使用:

1.Upload上传组件功能: 文件校验 : 文件格式校验/文件大小校验/上传文件总个数校验 相关功能 : 拖拽功能/上传到远程(七牛)/文件删除及下载 2.组件效果展示: 3.疑难点及解决方案: Promise.all多文件并行上传到远程(七牛云): (1)在beforeUpload钩子函数中获取token (2)循环fi…

Linux环境基础开发工具->vim

引入&#xff1a;vim是什么&#xff1f; vs叫作继承开发环境&#xff0c;我们可以在里面编辑代码&#xff0c;调式代码&#xff0c;运行代码....这种叫集成开发环境&#xff1b;而vim只用来编辑代码&#xff0c;也就是类似于在windows上打开一个记事本来写代码的操作 集成开发…

跳板问题(贪心算法+细节思考)

首先直接看题&#xff1a; 这题直接贪心其实问题不大&#xff1a; 下面先展示我的一个错误代码&#xff1a; # include<iostream> # include<vector> # include<algorithm>using namespace std;int main() {int N,M;cin>>N>>M;vector<vecto…

RuoYi前后端分离框架集成UEditorPlus富文本编辑器

一、背景 采用若依框架搭建了一个小型的电子书项目,项目前端、后端、移动端就一人,电子书的章节内容是以富文本内容进行呈现的,产品设计人员直接给了一个第三方收费的富文本编辑器截图放到开发文档中,提了一沓需求点,概况下来就是要做成下图中的样子。作为一个后端开发人…

IPD流程落地:项目任务书Charter开发

目录 简介 第一个方面&#xff0c;回答的是Why的问题。 第二点&#xff0c;要回答做什么的问题&#xff0c;也就是产品定义What的问题。 第三点就是要回答执行策略与计划的问题&#xff0c;也就是How、When、Who的问题。 第四点是对上述这些分析的总结分析&#xff0c;要为…

Vue 2 混入 (Mixins) 的详细使用指南

1.基本概念 混入 (Mixins) 是 Vue 2 中用于组件代码复用的重要特性&#xff0c;它允许你将可复用的功能分发到多个组件中。 混入是一种灵活的代码复用方式&#xff0c;可以包含任意组件选项&#xff08;data、methods、生命周期钩子等&#xff09;。当组件使用混入时&#xff…

FreeRTOS 在物联网传感器节点的应用:低功耗实时数据采集与传输方案

FreeRTOS 在物联网传感器节点的应用&#xff1a;低功耗实时数据采集与传输方案 二、FreeRTOS 任务划分与优先级设计 任务名称优先级执行周期功能描述Sensor_Collect3100ms多传感器数据采集与预处理Data_Process2事件驱动数据滤波/压缩/异常检测LoRa_Transmit41s低功耗长距离数…

Linux环境下基于Docker安装 PostgreSQL数据库并配置 pgvector

文章目录 1 docker-compose 安装 PostgreSQL 容器内安装 pgvector1.1 基于 docker-compose 安装 PostgreSQL 数据库1.2 容器内配置 pgvector 2. docker-compose Dockerfile 形式直接配置PostgreSQL数据库及 pgvector参考资料 PostgreSQL是一种功能强大的开源关系数据库管理系…

(9)-java+ selenium->元素定位之By name

1.简介 上一篇已经介绍了通过id来定位元素,继续介绍其他剩下的七种定位方法中的通过name来定位元素。本文来介绍Webdriver中元素定位方法之By name,顾名思义,就是我们想要定位的目标元素节点上,有一个name ="value"的属性,这样我们就可以通过name的value直接去…

深浅拷贝?

一、定义&#xff1a; 浅拷贝&#xff1a;只复制对象的第一层属性&#xff0c;若第一层属性是引用类型&#xff08;如对象、数组&#xff09;&#xff0c;则复制其内存地址&#xff0c;修改拷贝后的嵌套对象会影响原对象。 深拷贝&#xff1a;递归复制对象的所有层级&#xf…

Beckhoff PLC 功能块 FB_CTRL_ACTUAL_VALUE_FILTER (模拟量滤波)

1. 功能块概览 名称&#xff1a;FB_CTRL_ACTUAL_VALUE_FILTER&#xff08;实际值滤波控制功能块&#xff09;。作用&#xff1a;对测量输入值进行合理性检查&#xff08; plausibility check &#xff09;和滤波处理&#xff0c;防止异常跳变&#xff08;如传感器信号突变&…

【C++指南】string(四):编码

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 引言 在 C 编程中&#xff0c;处理字符串是一项极为常见的任务。而理解字符串在底层是如何编码存储的&…

深度学习之序列建模的核心技术:LSTM架构深度解析与优化策略

LSTM深度解析 一、引言 在深度学习领域&#xff0c;循环神经网络&#xff08;RNN&#xff09;在处理序列数据方面具有独特的优势&#xff0c;例如语音识别、自然语言处理等任务。然而&#xff0c;传统的 RNN 在处理长序列数据时面临着严重的梯度消失问题&#xff0c;这使得网…