leetCode-热题100-二叉树合集(JavaScript)

二叉树算法题解集合

1-二叉树的中序遍历

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

bash 复制代码
var inorderTraversal = function(root) {
    let res = []
    const dfs = (root)=>{
        if(root === null) return
        dfs(root.left)
        res.push(root.val)
        dfs(root.right)
    }
    dfs(root)
    return res
};

2-二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

bash 复制代码
var maxDepth = function(root) {
    // 层序遍历
    // num记录深度
    // 使用队列记录(队列的特点是先进先出)
    let num = 0
    let queue = [root]
    while(queue.length && root){
        let n = queue.length  // n记录当前层待遍历的节点数量
        while(n--){
        let node = queue.shift() // 取队列头部的节点
        // 若当前节点有左孩子,加入队列(下一层待遍历的节点)
        node.left && queue.push(node.left)
        // 若当前节点有右孩子,加入队列(下一层待遍历的节点)
        node.right && queue.push(node.right)
        }
        //当前层遍历完毕,深度加1
        num++
    }
    return num
};

深度是 "从根往下数",高度是 "从叶往上数"

自上而下(根 → 目标节点) 自下而上(目标节点 → 叶子)

使用前序求的就是深度,使用后序求的是高度 整棵二叉树的最大深度,就是这棵树的高度

3-翻转二叉树

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

bash 复制代码
var invertTree = function(root) {
   // 层序遍历
   let queue = [root]
   while(queue.length && root){
    let n = queue.length
    while(n--){
        // 取出当前节点
        let node = queue.shift()
        // 交换当前节点的左右子节点
        let temp = node.left
        node.left = node.right
        node.right = temp
        // 下一层要处理的是"已经交换后的子节点"
        node.left && queue.push(node.left)
        node.right && queue.push(node.right)
    }
   }
   return root
};

把每一个节点的左右孩子交换

遍历到节点时直接交换它的左右子节点,再把交换后的左右子节点加入队列,继续处理下一层

层序遍历是从上往下,先交换左右节点,再翻转左右子树

bash 复制代码
var invertTree = function(root) {
   // 前序遍历
   if(!root) return null
   // 保留原来的右子树,方便后续对右子树做翻转
   const rightNode = root.right
   // 先翻转左子树,再赋值给右节点
   root.right = invertTree(root.left)
   // 先翻转右子树,再赋值给左节点
   root.left = invertTree(rightNode)
   return root
};

前序遍历是从下往上,先翻转左右子树再交换左右节点

如果是先翻转右子树,就要先暂存左节点,因为翻转后的右子树被赋值给左节点,会覆盖原来的左节点的值,找不到原来的左子树

4-对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

bash 复制代码
var isSymmetric = function(root) {
    // 层序遍历
    if(!root) return true
    // 将根节点的左右子节点入队
    let queue = []
    queue.push(root.left)
    queue.push(root.right)
    // 层序遍历:成对处理队列中的节点(每次出队2个,检查是否对称)
    while(queue.length){
        // 成对出队:每次取队列前两个节点(这是一对需要检查的对称节点)
        let leftNode = queue.shift()
        let rightNode = queue.shift()
        // 情况1:两个节点都为null → 对称,跳过后续检查,继续下一对
        if(leftNode === null && rightNode === null) continue
        // 情况2:一个为null,一个不为null → 不对称,直接返回false;两个节点都存在但值不相等,直接返回false
        if(leftNode === null || rightNode === null || leftNode.val !== rightNode.val) return false
        // 情况3:两个节点对称(都非null且值相等)→ 继续检查它们的子节点
        // 按对称规则入队下一层的待处理节点
        // 左->左 右->右 左->右 右->左
        queue.push(leftNode.left)
        queue.push(rightNode.right)
        queue.push(leftNode.right)
        queue.push(rightNode.left)
    }
    // 所有成对节点都检查完,没有发现不对称 → 返回true
    return true
};

对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的

层序遍历

bash 复制代码
var isSymmetric = function(root) {
    // 后序遍历思想,先递归处理所有子节点对,再回溯判断当前节点对
    if(!root) return true
    // 辅助递归函数
    const compareNode = (leftNode,rightNode)=>{
        // 判断结构是否对称,包含两种情况:
        // 1.两个节点都为空->对称true;
        // 2.一个节点为空,一个节点不为空->不对称false
        if(leftNode === null || rightNode === null) return leftNode === rightNode
        // 3.两个节点都存在判断值是否相等
        if(leftNode.val !== rightNode.val) return false
        // 当前节点对是对称的,单层递归逻辑,继续检查子节点
        // outSide:left.left和right。right
        // inSide:left.right和right.left
        let outSide = compareNode(leftNode.left, rightNode.right)
        let inSide = compareNode(leftNode.right, rightNode.left)
        // // 外侧和内侧都对称,才说明当前两个节点对称
        return outSide&&inSide
    }
    return compareNode(root.left,root.right)
};

用递归函数逐对检查对称位置的节点

5- 二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点

root 。 两节点之间路径的 长度 由它们之间边数表示。

bash 复制代码
var diameterOfBinaryTree = function(root) {
    // 记录最大直径
    // 二叉树的直径 = 所有节点的「左子树深度 + 右子树深度」的最大值
    let maxDepth = 0 
    // 计算节点的深度(边数),同时更新最大直径
    const getDepth = (node)=>{
        if(!node) return 0
        // 左子树深度
        const leftDepth = getDepth(node.left)
        // 右子树深度
        const rightDepth = getDepth(node.right)
        // 更新最大直径
        maxDepth = Math.max(maxDepth, leftDepth + rightDepth)
        // 返回当前节点的深度
        return 1 + Math.max(leftDepth,rightDepth)
    }
    getDepth(root)
    return maxDepth
};

二叉树的直径 = 所有节点的「左子树深度 + 右子树深度」的最大值

6-二叉树的层序遍历

bash 复制代码
var levelOrder = function(root) {
    // 层序遍历
    // res记录最终结果
    let res = []
    let queue = [root]
    // 循环入口条件queue.length和root不为null
    // 判断条件必须加上root,root = null时queue=[null],queue.length > 0
    // 不加root会出现找不到node.val的情况
    while(queue.length && root){
        // 当前层待遍历的节点数量
        let n = queue.length
        // 记录当前层遍历的值
        let curLevel = []
        while(n--){
            let node = queue.shift()
            curLevel.push(node.val)
            node.left && queue.push(node.left)
            node.right && queue.push(node.right)
        }
        // 把遍历完的层的节点记录加入最终记录
        res.push(curLevel)
    }
    return res
};

7-将有序数组转化为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。

bash 复制代码
var sortedArrayToBST = function(nums) {
    // 中序遍历 BST 的结果是升序序列
    // 左子树所有节点值 < 根节点值;右子树所有节点值 > 根节点值
    // 选择数组中间元素作为根节点
    // 辅助递归函数
    const buildTree =(arr,left,right)=>{
        // 递归终止条件:左边界>右边界,无可构建的节点
        if (left > right) return null
        // 取数组中间元素作为根节点
        let mid = Math.floor(left+(right-left)/2)
        // 递归构建左右左子树
        // 左子树的数组索引范围[left,mid-1]
        let root = new TreeNode(arr[mid])
        root.left = buildTree(arr,left,mid-1)
        // 右子树的数组索引范围[mid+1,right]
        root.right = buildTree(arr,mid+1,right)
        // 返回当前子树的根节点
        return root
    }
    return buildTree(nums,0,nums.length-1)
};

二叉搜索树(BST)的关键特性

  • 左子树所有节点值 < 根节点值;
  • 右子树所有节点值 > 根节点值;
  • 中序遍历 BST的结果是升序序列(反过来:升序数组可以完美对应 BST 的中序遍历)。
    最优策略是:选数组的中间元素作为根节点
  1. 选中间元素当根节点:让左右子树节点数均衡,保证平衡;
  2. 递归构建左子树:处理数组「左半部分」,作为根节点的左子树;
  3. 递归构建右子树:处理数组「右半部分」,作为根节点的右子树;
  4. 终止条件:当左边界 > 右边界时,说明当前子树无节点,返回 null。

8-验证二叉搜索树

bash 复制代码
var isValidBST = function(root) {
    // 借助数组辅助
    // 二叉搜索树的中序遍历会得到升序数组
    // 判断中序遍历得到的数组是否升序即可
    let arr = []
    const buildArr = (root)=>{
        // 中序遍历
        if(root){
            buildArr(root.left)
            arr.push(root.val)
            buildArr(root.right)
        }
    }
    buildArr(root)
    for(let i = 0; i < arr.length; i++){
        // 非升序(小于或者等于)
        if(arr[i] <= arr[i-1]){
            return false
        }
    }
    return true
};
bash 复制代码
var isValidBST = function(root) {
    // 递归函数判断
    // 当前节点的值必须大于其左子树中所有节点的值,并且小于其右子树中所有节点的值
    const validate = (node,max,min)=>{
        // 空节点符合
        if(node === null) return true
        // 节点值不符合,当前节点值必须在 (min, max) 范围内
        if(node.val >= max || node.val <= min) return false
        // 对左子树进行递归,更新最大值为当前节点值。
        // 对右子树进行递归,更新最小值为当前节点值。
        return validate(node.left,node.val,min) && validate(node.right,max,node.val)
    }
    // 最大值正无穷,最小值负无穷
    return validate(root,Infinity,-Infinity)
};

递归判断( 当前节点的值必须大于其左子树中所有节点的值,并且小于其右子树中所有节点的值。 )

  • 对左子树进行递归,更新最大值为当前节点值。
  • 对右子树进行递归,更新最小值为当前节点值。

9-二叉搜索树中第k小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(k 从 1 开始计数)。

bash 复制代码
var kthSmallest = function(root, k) {
    // 借助数组辅助
    // 二叉搜索树的中序遍历可以得到一个升序数组
    let arr = []
    const buildArr = (root)=>{
        if(root){
            buildArr(root.left)
            arr.push(root.val)
            buildArr(root.right)
        }
    }
    buildArr(root)
    // k从1开始计数,数组索引从0开始
    return arr[k-1]
};

和上一道题思路一致

10-二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

bash 复制代码
var rightSideView = function(root) {
    // 层序遍历,记录每一层的最后一个节点
    // 二叉树的「右视图」本质是:每一层的最后一个节点的值组成的数组
    let res = []
    let queue = [root]
    while(queue.length && root){
        let n = queue.length
        while(n--){
            let node = queue.shift()
            // 记录当前层的最后一个节点值
            if(n === 0){
                res.push(node.val)
            }
            // 入队下一层待遍历的节点
            node.left && queue.push(node.left)
            node.right && queue.push(node.right)
        }
    }
    return res
};

二叉树的「右视图」本质是:每一层的最后一个节点的值组成的数组

11-二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。
bash 复制代码
var flatten = function(root) {
    // 先序遍历顺序是「根→左→右」,它的左子树所有节点,必须出现在「根」之后、「原右子树」之前;
    // 需要找到左子树的「最后一个节点」(先序遍历的最后一个),把原右子树接在它后面
    // curr记录当前子树的根节点
    let curr = root
    while(curr){
        // 当左子树存在时
        if(curr.left){
            // predecessor记录当前左子树的最右侧节点
            let predecessor = curr.left
            while(predecessor.right){
                predecessor = predecessor.right
            }
            // 将原来的右子树挂载到最右侧节点的右子树上
            predecessor.right = curr.right
            // 将修改后的原左子树移动到原来右子树的位置
            curr.right = curr.left
            // 原左子树节点值置空
            curr.left = null
        }
        // 一直向右修改遍历
        curr = curr.right
    }
};

先序遍历

12-从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder是同一棵树的中序遍历,请构造二叉树并返回其根节点。

bash 复制代码
var buildTree = function(preorder, inorder) {
   // 前序遍历:根左右;中序遍历:左根右
   // 取前序遍历序列的第一个元素作为根节点,找到其在中序遍历序列中的索引,左边为左子树序列,右边为右子树序列
   if(preorder.length === 0) return null
   // 先序遍历序列的第一个元素是根节点的值
   const rootVal = preorder.shift()
   // 找到根节点在中序遍历序列中的索引,中序遍历中根节点左边的为左子树[0,rootIdx-1],右边为右子树[rootIdx+1,inorder.length-1]
   const rootIdx = inorder.indexOf(rootVal)
   const root = new TreeNode(rootVal)
   //分割左子树和右子树
   // slice(start,end):start和end的可选,start默认为0,end默认为数组长度,取值范围为[start,end)
   root.left = buildTree(preorder.slice(0,rootIdx),inorder.slice(0,rootIdx))
   // 由于第一步取先序遍历序列的第一个元素为根节点,先序遍历序列长度-1
   // 先序遍历序列中,左子树[0,rootIdx-1],右子树为[rootIdx,preorder.length-1]
   root.right = buildTree(preorder.slice(rootIdx),inorder.slice(rootIdx+1))
   return root
};
  • 前序遍历的顺序是「根节点 → 左子树 → 右子树」,所以前序数组的第一个元素一定是当前子树的根节点。
  • 中序遍历的顺序是「左子树 → 根节点 → 右子树」,所以找到根节点在中序数组中的位置后,左侧是左子树的中序数组,右侧是右子树的中序数组。

13-路径总和Ⅲ

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

bash 复制代码
var pathSum = function(root, targetSum) {
    // 前缀和:使用哈希表
    // 如果两段路径的总和的差值 = targetNum ; 那么之间的子路径就是一条目标路径
    // 哈希表中存放的是从根到当前节点的路径上,所有已经走过的节点的前缀和,以及每个前缀和出现了多少次
    // key是前缀和,value是前缀和出现的次数
    // count:符合条件的目标路径的条数
    const prefixSumMap = new Map()
    prefixSumMap.set(0,1)
    const backtrack = (node,currentSum)=>{
        if(!node) return 0
        // currentSum当前节点的前缀和
        currentSum += node.val
        // 找历史前缀和 = currentSum - targetSum,统计次数
        let count = prefixSumMap.get(currentSum-targetSum) || 0
        //  更新哈希表:当前前缀和的次数+1
        prefixSumMap.set(currentSum, (prefixSumMap.get(currentSum) || 0)+1)
        // 递归遍历左右子树,累加路径数
        count += backtrack(node.left,currentSum)
        count += backtrack(node.right,currentSum)
        // 回溯:恢复哈希表(当前节点遍历完,移除其前缀和)
        prefixSumMap.set(currentSum, (prefixSumMap.get(currentSum) || 0)-1)
        // 8. 返回当前节点的总路径数
        return count
    }
    return backtrack(root,0)
};

这个题有点绕,需要思考

初始化哈希表:防止漏掉只有一个节点,节点值=targetNum的情况

14-二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。"

bash 复制代码
var lowestCommonAncestor = function(root, p, q) {
    // 递归函数辅助
    const travelTree = (node,p,q)=>{
        // 递归终止条件
        if(node === null || node === p || node === q){
            return node
        }
        // 后序遍历,在左右子树中寻找是否有p和q节点
        let left = travelTree(node.left,p,q)
        let right = travelTree(node.right,p,q)

        // 单层递归逻辑
        // 如果左子树和右子树中各找到一个目标节点,当前节点就是最近公共祖先
        if(left !== null && right !== null){
            return node
        }
        // 如果左子树中没找到目标节点,目标节点在右子树,返回右子树
        if(left === null){
            return right
        }
        // 右子树没找到,目标节点都在左子树,返回左子树的结果
        return left
    }
    return travelTree(root,p,q)
};

这个有点绕,需要仔细思考

15-二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个节点,且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root ,返回其 最大路径和

bash 复制代码
var maxPathSum = function(root) {
    // 如果有一条合法的路径,那一定有一个最高点(最接近根节点的)
    // 最高转折点可以同时向它的左右子树延申,其他节点只能选一边加入路径
    // 初始化最大路径和为负无穷
    let maxSum = -Infinity
    // 递归函数计算最大路径和
    const maxGain = (node)=>{
        // 节点为空,贡献值为0
        if(node === null) return 0
        // 计算当前节点的左子树的贡献值,和0比较,节点值可能为负数
        let leftGain = Math.max(maxGain(node.left),0)
        // 计算当前节点的右子树的贡献值
        let rightGain = Math.max(maxGain(node.right),0)
        // 计算当前节点作为最高转折点的路径和
        // 左子树贡献 + 当前节点值 + 右子树贡献
        let currentPathSum = node.val + leftGain + rightGain
        // 更新最大路径和
        maxSum = Math.max(currentPathSum,maxSum)
        // 返回节点值提供给其父节点的最大贡献
        // 只能选左或右其中一条更长的路径
        return node.val+Math.max(leftGain,rightGain)
    }
    maxGain(root)
    return maxSum
};
相关推荐
闲看云起10 小时前
LeetCode-day5:三数之和
算法·leetcode·职场和发展
Xの哲學10 小时前
Linux 文件系统一致性: 从崩溃恢复到 Journaling 机制
linux·服务器·算法·架构·边缘计算
冴羽11 小时前
2025 年最火的前端项目出炉,No.1 易主!
前端·javascript·node.js
wtmReiner11 小时前
山东大学数值计算2026.1大三上期末考试回忆版
笔记·算法
黛色正浓11 小时前
leetCode-热题100-滑动窗口合集(JavaScript)
javascript·算法·leetcode
asdfg125896311 小时前
小程序开发中的JS和Go的对比及用途
开发语言·javascript·golang
漫随流水11 小时前
leetcode算法(145.二叉树的后序遍历)
数据结构·算法·leetcode·二叉树
Tony_yitao11 小时前
22.华为OD机试真题:数组拼接(Java实现,100分通关)
java·算法·华为od·algorithm
2501_9418752811 小时前
在东京复杂分布式系统中构建统一可观测性平台的工程设计实践与演进经验总结
c++·算法·github
sonadorje11 小时前
梯度下降法的迭代步骤
算法·机器学习