当前位置: 首页 > news >正文

《代码随想录》二叉树专题算法笔记

《代码随想录》二叉树专题算法笔记

本文整理《代码随想录》中二叉树专题的经典算法题目,包含题目要点分析与完整代码实现,覆盖算法训练营 Day13 至 Day21 打卡内容,涵盖二叉树的遍历、构造、修改、查询等核心场景。

一、二叉树的遍历

1. 二叉树的递归遍历(Day13-01)

核心要点
  • 二叉树定义:每个节点包含 val(值)、left(左子节点)、right(右子节点)。
  • 递归遍历本质:通过调整 “处理当前节点” 与 “递归左右子树” 的顺序,实现前序、中序、后序遍历:
    • 前序遍历:处理当前节点 → 递归左子树 → 递归右子树(根左右)。
    • 中序遍历:递归左子树 → 处理当前节点 → 递归右子树(左根右)。
    • 后序遍历:递归左子树 → 递归右子树 → 处理当前节点(左右根)。
  • 终止条件:当 root == null 时,直接返回(递归边界)。
核心代码(以中序遍历为例)
// 假设result为存储遍历结果的List<Integer>
public void inorder(TreeNode root, List<Integer> result) {if (root == null) {return;}// 中序遍历:左 → 根 → 右(调整顺序可切换为前/后序)inorder(root.left, result);   // 递归左子树result.add(root.val);         // 处理当前节点inorder(root.right, result);  // 递归右子树
}

2. 二叉树的层序遍历(Day13-02)

核心要点
  • 层序遍历:按 “从上到下、从左到右” 的顺序遍历每一层节点,需借助队列(先进先出特性)实现。
  • 关键逻辑:
    1. 初始化队列,将根节点入队。
    2. 外层循环:队列不为空时,获取当前层的节点数量 size(确保内层循环只处理当前层节点)。
    3. 内层循环:遍历当前层 size 个节点,将节点值存入临时列表,同时将其左右子节点(非空)入队。
    4. 将当前层的临时列表加入结果集,直至队列空。
代码实现
class Solution {// 存储最终层序遍历结果(二维列表:每层一个子列表)List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> levelOrder(TreeNode root) {checkFun(root);return res;}// 辅助方法:实现层序遍历逻辑public void checkFun(TreeNode root) {if (root == null) return; // 根为空,直接返回Queue<TreeNode> que = new LinkedList<>();que.offer(root); // 根节点入队while (!que.isEmpty()) {List<Integer> item = new ArrayList<>(); // 存储当前层节点值int size = que.size(); // 当前层节点数量(固定,避免后续入队影响)// 遍历当前层所有节点while (size-- > 0) {TreeNode node = que.poll(); // 出队当前节点item.add(node.val);         // 记录节点值// 左右子节点非空则入队(保证下一层遍历)if (node.left != null) que.offer(node.left);if (node.right != null) que.offer(node.right);}res.add(item); // 当前层加入结果集}}
}

3. 二叉树的层序遍历 II(Day13-03)

核心要点
  • 需求:层序遍历结果 “从下到上”(即逆序输出层序遍历结果)。
  • 实现思路:在普通层序遍历基础上,最后对结果集进行反转(或遍历过程中从头部插入结果)。
代码实现
class Solution {List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> levelOrderBottom(TreeNode root) {checkFun(root);return res;}public void checkFun(TreeNode root) {List<List<Integer>> curRes = new ArrayList<>(); // 临时存储正序层序结果if (root == null) return;Queue<TreeNode> que = new LinkedList<>();que.offer(root);while (!que.isEmpty()) {List<Integer> item = new ArrayList<>();int size = que.size();while (size-- > 0) {TreeNode node = que.poll();item.add(node.val);if (node.left != null) que.offer(node.left);if (node.right != null) que.offer(node.right);}curRes.add(item); // 正序存储每层}// 反转正序结果,得到“从下到上”的层序遍历for (int i = curRes.size() - 1; i >= 0; i--) {res.add(curRes.get(i));}}
}

二、二叉树的修改与判断

1. 翻转二叉树(Day14-01)

核心要点
  • 需求:交换每个节点的左右子节点(即 “镜像翻转”)。
  • 实现思路:采用前序 / 后序遍历(中序遍历需注意顺序,可能导致重复交换),递归交换每个节点的左右子树。
  • 关键逻辑:先交换当前节点的左右子节点,再递归交换左右子树(前序);或先递归交换左右子树,再交换当前节点(后序)。
代码实现
class Solution {public TreeNode invertTree(TreeNode root) {if (root == null) return root; // 空树直接返回// 前序遍历:先交换当前节点,再递归左右子树(后序需调整顺序)swap(root);                  // 交换当前节点的左右子节点invertTree(root.left);       // 递归翻转左子树invertTree(root.right);      // 递归翻转右子树return root;}// 辅助方法:交换节点的左右子节点public void swap(TreeNode root) {TreeNode temp = root.left;root.left = root.right;root.right = temp;}
}

2. 对称二叉树(Day14-02)

核心要点
  • 需求:判断二叉树是否 “对称”(即左右子树互为镜像)。
  • 递归思路:比较左子树的左节点右子树的右节点(外侧)、左子树的右节点右子树的左节点(内侧),需覆盖所有节点为空 / 非空的情况。
  • 终止条件(节点对比逻辑):
    1. 左空、右非空 → 不对称(返回 false)。
    2. 左非空、右空 → 不对称(返回 false)。
    3. 左空、右空 → 对称(返回 true)。
    4. 左值 ≠ 右值 → 不对称(返回 false)。
代码实现
class Solution {public boolean isSymmetric(TreeNode root) {// 空树对称;非空树需比较左右子树是否对称return root == null ? true : compare(root.left, root.right);}// 辅助方法:比较两个节点是否对称(递归核心)public boolean compare(TreeNode left, TreeNode right) {// 1. 处理节点为空的情况if (left == null && right != null) return false;if (left != null && right == null) return false;if (left == null && right == null) return true;// 2. 节点非空,比较值是否相等if (left.val != right.val) return false;// 3. 递归比较外侧(左左 vs 右右)和内侧(左右 vs 右左)boolean outside = compare(left.left, right.right); // 外侧boolean inside = compare(left.right, right.left);  // 内侧return outside && inside; // 两者都对称才返回true}
}

3. 二叉树的最大深度(Day14-03)

核心要点
  • 定义:二叉树的最大深度是 “从根节点到最远叶子节点的最长路径上的节点数”。
  • 递归思路:采用后序遍历,先求左子树深度和右子树深度,当前节点的深度 = 左右子树深度的最大值 + 1(+1 表示当前节点本身)。
  • 终止条件:空节点的深度为 0。
代码实现
class Solution {public int maxDepth(TreeNode root) {if (root == null) return 0; // 空节点深度为0// 后序遍历:左 → 右 → 根(先求左右子树深度)int leftHeight = maxDepth(root.left);  // 左子树深度int rightHeight = maxDepth(root.right); // 右子树深度// 当前节点深度 = 左右子树最大深度 + 1return Math.max(leftHeight, rightHeight) + 1;}
}

4. 二叉树的最小深度(Day14-04)

核心要点
  • 定义:二叉树的最小深度是 “从根节点到最近叶子节点的最短路径上的节点数”。
  • 与最大深度的区别:需排除 “单支子树” 的情况(如左子树为空、右子树非空时,最小深度是右子树深度 + 1,而非 1)。
  • 关键逻辑:后序遍历中,若左子树为空则返回右子树深度 + 1,若右子树为空则返回左子树深度 + 1,否则返回左右子树最小深度 + 1。
代码实现
class Solution {public int minDepth(TreeNode root) {if (root == null) return 0; // 空节点深度为0// 后序遍历:先求左右子树深度int minLeft = minDepth(root.left);int minRight = minDepth(root.right);// 处理单支子树情况(排除非叶子节点的“空分支”)if (root.left == null && root.right != null) {return minRight + 1;}if (root.left != null && root.right == null) {return minLeft + 1;}// 左右子树都非空,返回最小深度 + 1return Math.min(minLeft, minRight) + 1;}
}

5. 平衡二叉树(Day15-01)

核心要点
  • 定义:平衡二叉树(AVL 树)是 “所有节点的左右子树高度差的绝对值 ≤ 1” 的二叉树。

  • 递归思路:采用

    后序遍历

    ,在求子树高度的同时判断是否平衡:

    1. 若子树不平衡,返回 -1(标记不平衡状态)。
    2. 若子树平衡,返回子树的高度(用于上层判断)。
  • 关键逻辑:若左右子树高度差 > 1,返回 -1;否则返回当前子树高度(左右子树最大高度 + 1)。

代码实现
class Solution {public boolean isBalanced(TreeNode root) {// 若getHeight返回-1,说明树不平衡;否则平衡return getHeight(root) != -1;}// 辅助方法:求子树高度,若不平衡返回-1public int getHeight(TreeNode root) {if (root == null) return 0; // 空节点高度为0// 后序遍历:先求左右子树高度int leftHeight = getHeight(root.left);if (leftHeight == -1) return -1; // 左子树不平衡,直接返回-1int rightHeight = getHeight(root.right);if (rightHeight == -1) return -1; // 右子树不平衡,直接返回-1// 判断当前节点是否平衡if (Math.abs(leftHeight - rightHeight) > 1) {return -1; // 不平衡,返回-1}// 平衡,返回当前子树高度return Math.max(leftHeight, rightHeight) + 1;}
}

三、二叉树的路径与求和

1. 二叉树的所有路径(Day15-02)

核心要点
  • 需求:输出从根节点到所有叶子节点的路径(格式如 1->2->3)。

  • 思路:

    回溯算法

    (前序遍历 + 路径回溯):

    1. 用列表 path 记录当前路径,用列表 res 记录所有路径。
    2. 前序遍历:先将当前节点加入 path,若为叶子节点则拼接路径并加入 res
    3. 递归左右子树后,需 “回溯”(删除 path 最后一个元素),避免路径污染。
代码实现
class Solution {// 全局变量:存储所有路径(最终结果)List<String> res = new ArrayList<>();// 全局变量:存储当前路径(节点值)List<Integer> path = new ArrayList<>();public List<String> binaryTreePaths(TreeNode root) {if (root == null) return res;backtracking(root); // 回溯遍历所有路径return res;}// 回溯方法:遍历从当前节点到叶子节点的路径public void backtracking(TreeNode root) {path.add(root.val); // 前序:先加入当前节点// 终止条件:当前节点是叶子节点(左右子树都为空)if (root.left == null && root.right == null) {StringBuilder sb = new StringBuilder();// 拼接路径(除最后一个节点外,每个节点后加"->")for (int i = 0; i < path.size() - 1; i++) {sb.append(path.get(i)).append("->");}sb.append(path.get(path.size() - 1)); // 拼接最后一个节点res.add(sb.toString()); // 加入结果集return;}// 递归左子树(非空才处理)if (root.left != null) {backtracking(root.left);path.remove(path.size() - 1); // 回溯:删除左子树的节点}// 递归右子树(非空才处理)if (root.right != null) {backtracking(root.right);path.remove(path.size() - 1); // 回溯:删除右子树的节点}}
}

2. 左叶子之和(Day15-03)

核心要点
  • 定义:左叶子是 “父节点的左子节点,且自身没有左右子节点” 的节点。
  • 思路:后序遍历,先求左子树的左叶子和,再求右子树的左叶子和,最后相加。
  • 关键逻辑:判断当前节点的左子节点是否为左叶子(左子节点非空,且左子节点的左右子树都为空),若是则累加其值。
代码实现
class Solution {public int sumOfLeftLeaves(TreeNode root) {if (root == null) return 0; // 空树和为0if (root.left == null && root.right == null) return 0; // 叶子节点无左叶子// 后序遍历:左 → 右 → 根int leftSum = sumOfLeftLeaves(root.left); // 左子树的左叶子和// 判断当前节点的左子节点是否为左叶子if (root.left != null && root.left.left == null && root.left.right == null) {leftSum = root.left.val; // 是左叶子,直接取其值}int rightSum = sumOfLeftLeaves(root.right); // 右子树的左叶子和return leftSum + rightSum; // 总左叶子和}
}
  • Day16

    1. 找树左下角的值

    方法一:递归(DFS + 回溯)
    • 要点:
      • 使用两个全局变量:val 存储当前最左下角的值,Deep 存储其深度。
      • 只有当当前节点是叶子节点且深度大于 Deep 时才更新。
      • 使用回溯维护当前深度。
    class Solution {int val = 0;int Deep = -1;public int findBottomLeftValue(TreeNode root) {val = root.val;backtracking(root, 0);return val;}public void backtracking(TreeNode root, int deep) {if (root == null) return;if (root.left == null && root.right == null) {if (deep > Deep) {Deep = deep;val = root.val;}}if (root.left != null) {deep += 1;backtracking(root.left, deep);deep -= 1;}if (root.right != null) {deep += 1;backtracking(root.right, deep);deep -= 1;}}
    }
    
    方法二:层序遍历(BFS)
    • 要点:
      • 每层第一个节点即为该层最左边的节点。
      • 最后一层的第一个节点即为答案。
    class Solution {public int findBottomLeftValue(TreeNode root) {Queue<TreeNode> que = new LinkedList<>();que.offer(root);int res = 0;while (!que.isEmpty()) {int size = que.size();for (int i = 0; i < size; i++) {TreeNode temp = que.poll();if (i == 0) res = temp.val;if (temp.left != null) que.offer(temp.left);if (temp.right != null) que.offer(temp.right);}}return res;}
    }
    

    2. 路径总和

    • 要点:
      • 递归判断是否存在从根到叶子节点的路径,使得路径上所有节点值之和等于 targetSum
      • 使用回溯 + 剪枝(一旦找到就返回)。
    class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {if (root == null) return false;if (root.left == null && root.right == null) {return targetSum == root.val;}if (root.left != null) {if (hasPathSum(root.left, targetSum - root.left.val)) return true;}if (root.right != null) {if (hasPathSum(root.right, targetSum - root.right.val)) return true;}return false;}
    }
    

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

    • 要点:
      • 后序最后一个元素为根。
      • 在中序中找到根的位置,分割左右子树。
      • 递归构建左右子树(使用左闭右开区间)。
    class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) {if (postorder.length == 0) return null;return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);}public TreeNode buildHelper(int[] inorder, int inStart, int inEnd,int[] postorder, int postStart, int postEnd) {if (postStart == postEnd) return null;int rootVal = postorder[postEnd - 1];TreeNode root = new TreeNode(rootVal);int index;for (index = inStart; index < inEnd; index++) {if (inorder[index] == rootVal) break;}int leftInStart = inStart;int leftInEnd = index;int rightInStart = index + 1;int rightInEnd = inEnd;int leftPostStart = postStart;int leftPostEnd = postStart + (leftInEnd - leftInStart);int rightPostStart = leftPostEnd;int rightPostEnd = postEnd - 1;root.left = buildHelper(inorder, leftInStart, leftInEnd,postorder, leftPostStart, leftPostEnd);root.right = buildHelper(inorder, rightInStart, rightInEnd,postorder, rightPostStart, rightPostEnd);return root;}
    }
    

    Day17

    1. 最大二叉树

    • 要点:
      • 找到数组最大值作为根,递归构建左右子树。
      • 使用左闭右开区间。
    class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {return constructMaximumBinaryTree1(nums, 0, nums.length);}public TreeNode constructMaximumBinaryTree1(int[] nums, int left, int right) {if (left == right) return null;if (right - left == 1) return new TreeNode(nums[left]);int maxVal = nums[left], index = left;for (int i = left + 1; i < right; i++) {if (nums[i] > maxVal) {maxVal = nums[i];index = i;}}TreeNode root = new TreeNode(maxVal);root.left = constructMaximumBinaryTree1(nums, left, index);root.right = constructMaximumBinaryTree1(nums, index + 1, right);return root;}
    }
    

    2. 合并二叉树

    • 要点:
      • 同时遍历两棵树,合并到 root1 上。
      • 若某节点为空,直接返回另一棵树对应节点。
    class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if (root1 == null) return root2;if (root2 == null) return root1;root1.val += root2.val;root1.left = mergeTrees(root1.left, root2.left);root1.right = mergeTrees(root1.right, root2.right);return root1;}
    }
    

    3. 二叉搜索树中的搜索

    • 要点:
      • 利用 BST 性质:左小右大。
      • 可用递归或迭代。
    class Solution {public TreeNode searchBST(TreeNode root, int val) {while (root != null) {if (root.val > val) root = root.left;else if (root.val < val) root = root.right;else return root;}return null;}
    }
    

    4. 验证二叉搜索树

    • 要点:
      • 中序遍历应为严格递增序列。
      • 使用 pre 指针记录前一个节点。
    class Solution {TreeNode pre = null;public boolean isValidBST(TreeNode root) {if (root == null) return true;boolean left = isValidBST(root.left);if (pre != null && pre.val >= root.val) return false;pre = root;boolean right = isValidBST(root.right);return left && right;}
    }
    

    Day18

    1. 二叉搜索树的最小绝对差

    • 要点:
      • 中序遍历相邻节点差值最小。
      • 全局变量 premin
    class Solution {int min = Integer.MAX_VALUE;TreeNode pre = null;public int getMinimumDifference(TreeNode root) {if (root == null) return 0;getMinimumDifference(root.left);if (pre != null) {min = Math.min(min, root.val - pre.val);}pre = root;getMinimumDifference(root.right);return min;}
    }
    

    2. 二叉搜索树中的众数

    • 要点:
      • 中序遍历统计频率。
      • 使用 countmaxCountpreres 四个全局变量。
    class Solution {TreeNode pre = null;int count = 0;int maxCount = 0;List<Integer> res = new ArrayList<>();public int[] findMode(TreeNode root) {findMode1(root);return res.stream().mapToInt(i -> i).toArray();}public void findMode1(TreeNode root) {if (root == null) return;findMode1(root.left);if (pre == null || pre.val != root.val) count = 1;else count++;if (count == maxCount) res.add(root.val);else if (count > maxCount) {res.clear();maxCount = count;res.add(root.val);}pre = root;findMode1(root.right);}
    }
    

    3. 二叉树的最近公共祖先

    • 要点:
      • 后序遍历(自底向上)。
      • 若左右子树分别包含 pq,则当前节点为 LCA。
    class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null || root == p || root == q) return root;TreeNode left = lowestCommonAncestor(root.left, p, q);TreeNode right = lowestCommonAncestor(root.right, p, q);if (left == null && right == null) return null;if (left != null && right == null) return left;if (left == null && right != null) return right;return root;}
    }
    

    Day20

    1. 二叉搜索树的最近公共祖先

    • 要点:
      • 利用 BST 性质:若 root.valpq 之间,则为 LCA。
      • 可用迭代法高效实现。
    class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {while (root != null) {if (root.val > p.val && root.val > q.val) root = root.left;else if (root.val < p.val && root.val < q.val) root = root.right;else return root;}return root;}
    }
    

    2. 二叉搜索树中的插入操作

    • 要点:
      • 插入位置一定是叶子节点。
      • 递归到底后新建节点并返回。
    class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {if (root == null) return new TreeNode(val);if (root.val > val) root.left = insertIntoBST(root.left, val);if (root.val < val) root.right = insertIntoBST(root.right, val);return root;}
    }
    

3. 删除二叉搜索树中的节点

  • 要点:
    • 分四种情况处理:
      1. 节点无子节点 → 删除;
      2. 仅有左/右子树 → 返回子树;
      3. 有左右子树 → 将右子树最左节点接上左子树,返回右子树。
class Solution {public TreeNode deleteNode(TreeNode root, int key) {if (root == null) return null;if (root.val == key) {if (root.left == null && root.right == null) return null;else if (root.left != null && root.right == null) return root.left;else if (root.left == null && root.right != null) return root.right;else {TreeNode cur = root.right;while (cur.left != null) cur = cur.left;cur.left = root.left;return root.right;}}if (root.val < key) root.right = deleteNode(root.right, key);if (root.val > key) root.left = deleteNode(root.left, key);return root;}
}

Day21

1. 修剪二叉搜索树

  • 要点:
    • 前序遍历。
    • 若当前节点小于 low,则其左子树全无效,递归右子树;
    • 若大于 high,则右子树无效,递归左子树。
class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if (root == null) return null;if (root.val < low) return trimBST(root.right, low, high);if (root.val > high) return trimBST(root.left, low, high);root.left = trimBST(root.left, low, high);root.right = trimBST(root.right, low, high);return root;}
}

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

  • 要点:
    • 选中点为根,递归构建左右子树(左闭右闭)。
    • 保证树高度平衡。
class Solution {public TreeNode sortedArrayToBST(int[] nums) {return build(nums, 0, nums.length - 1);}public TreeNode build(int[] nums, int left, int right) {if (left > right) return null;int mid = (left + right) / 2;TreeNode root = new TreeNode(nums[mid]);root.left = build(nums, left, mid - 1);root.right = build(nums, mid + 1, right);return root;}
}

3. 把二叉搜索树转换为累加树

  • 要点:
    • 反向中序遍历(右 → 中 → 左)。
    • 累加右侧所有更大值。
class Solution {int pre = 0;public TreeNode convertBST(TreeNode root) {convertBST1(root);return root;}public void convertBST1(TreeNode root) {if (root == null) return;convertBST1(root.right);root.val += pre;pre = root.val;convertBST1(root.left);}
}
http://www.dtcms.com/a/434394.html

相关文章:

  • CSS3 用户界面
  • 虚幻引擎UE5专用服务器游戏开发-32 使用Gameplay Tags阻止连招触发
  • 鼠标垫东莞网站建设网站建设公司的公司
  • SOAR技术与高效网络安全运营
  • Node.js 本地服务部署、常驻及调用完整笔记
  • Java 工具类 Hutool、Guava 与 Apache Commons 的区别
  • 怎么创建网站挣钱济南产品网站建设外包
  • BEV query 式图片点云视觉特征融合
  • 操作系统应用开发(十二)RustDesk 用户服务器搭建——东方仙盟筑基期
  • 将若依(RuoYi)项目创建为私有Gitee仓库的完整步骤
  • 网站上的百度地图标注咋样做app开发制作专业吗
  • TCP 协议核心面试题 (附答案详解)
  • Spring Security 实战:彻底解决 CORS 跨域凭据问题与 WebSocket 连接失败
  • Tabby下载安装与连接服务器
  • Apache Beam入门教程:统一批流处理模型
  • 计算机毕业设计 基于Hadoop的信贷风险评估的数据可视化分析与预测系统 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 【QT常用技术讲解】QTablewidget单元格存储隐藏的数据
  • K8s学习笔记(九) job与cronjob
  • MATLAB线性代数函数完全指南
  • 关于单片机外设存储芯片的应用笔记(IIC驱动)
  • 梅州网站建设南宁网站 制作
  • 2015 年真题配套词汇单词笔记(考研真相)
  • 中国建设银行舟山分行网站网站构建的过程
  • python如何通过链接下载保存视频
  • K-Lite Mega/FULL Codec Pack(视频解码器)
  • SpringBoot+Vue医院预约挂号系统 附带详细运行指导视频
  • 85-dify案例分享-不用等 OpenAI 邀请,Dify+Sora2工作流实测:写实动漫视频随手做,插件+教程全送
  • GUI高级工程师面试题
  • 经典网站设计风格网站建设产品介绍
  • 基于单片机的人体心率、体温监测系统(论文+源码)