深入浅出Java算法树结构

深入浅出Java算法树结构

树结构算法题在面试中出现的频率特别高,今天我就用最通俗的大白话,给大家讲讲怎么搞定这些题目。

一、树题解题三板斧

遇到树的问题,先想这三招:

  1. 递归大法好:树天生适合递归,大部分问题都能用递归解决
  2. 迭代要用栈/队列:不想用递归?那就用栈或队列来模拟
  3. 分治思想:把大树拆成小树,分别解决再合并

二、高频考题解题套路

1. 求树的高度(最大深度)

问题:算算这棵树有几层?

递归思路

  • 如果树为空,高度为0
  • 否则高度 = 1 + max(左子树高度, 右子树高度)
java 复制代码
public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

白话解释:问一个节点有多高,它说:"我看看我左右两个孩子谁更高,我就比它高一截"

2. 判断平衡二叉树

问题:这棵树左右两边高度差不超过1吗?

解题思路

  • 左右子树高度差 ≤ 1
  • 且左右子树也都是平衡树
java 复制代码
public boolean isBalanced(TreeNode root) {
    if (root == null) return true;
    return Math.abs(height(root.left) - height(root.right)) <= 1 
           && isBalanced(root.left) 
           && isBalanced(root.right);
}

private int height(TreeNode node) {
    if (node == null) return 0;
    return 1 + Math.max(height(node.left), height(node.right));
}

优化版(避免重复计算):

java 复制代码
public boolean isBalanced(TreeNode root) {
    return checkHeight(root) != -1;
}

private int checkHeight(TreeNode node) {
    if (node == null) return 0;
    
    int left = checkHeight(node.left);
    if (left == -1) return -1;
    
    int right = checkHeight(node.right);
    if (right == -1) return -1;
    
    if (Math.abs(left - right) > 1) return -1;
    return Math.max(left, right) + 1;
}

白话解释:让每个节点汇报自己是否平衡,如果不平衡就向上传递"不合格"信号

3. 判断对称二叉树

问题:这棵树左右对称吗?

解题思路

  • 根节点的左右子树是镜像关系
  • 左子树的左孩子 = 右子树的右孩子
  • 左子树的右孩子 = 右子树的左孩子
java 复制代码
public boolean isSymmetric(TreeNode root) {
    if (root == null) return true;
    return isMirror(root.left, root.right);
}

private boolean isMirror(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return true;
    if (t1 == null || t2 == null) return false;
    return (t1.val == t2.val) 
           && isMirror(t1.left, t2.right) 
           && isMirror(t1.right, t2.left);
}

白话解释:就像照镜子,左边抬起右手,镜子里应该是左边抬起左手

4. 最近公共祖先(LCA)

问题:找两个节点的最近共同祖先

解题思路

  • 如果当前节点是p或q,返回当前节点
  • 在左右子树中查找
  • 如果左右都找到,当前节点就是LCA
  • 如果只有一边找到,返回那一边的结果
java 复制代码
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 root;
    return left != null ? left : right;
}

白话解释:问每个节点:"你下面有这两个人吗?如果左右都有,你就是他们最近的共同领导"

5. 路径总和

问题:有没有一条从根到叶子的路径,节点值加起来等于目标和?

解题思路

  • 从根节点开始,每次用目标和减去当前节点值
  • 到达叶子节点时,看剩余和是否等于叶子节点值
java 复制代码
public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null) return false;
    if (root.left == null && root.right == null && root.val == sum) return true;
    return hasPathSum(root.left, sum - root.val) 
           || hasPathSum(root.right, sum - root.val);
}

白话解释:带着钱从公司总部出发,每经过一个部门交一笔钱,到基层员工那里刚好把钱花完

三、二叉搜索树(BST)特殊题型

1. 验证BST

问题:这棵树真的是BST吗?

解题思路

  • 左子树所有节点 < 当前节点
  • 右子树所有节点 > 当前节点
  • 注意不能只比较父子节点!
java 复制代码
public boolean isValidBST(TreeNode root) {
    return isValid(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

private boolean isValid(TreeNode node, long min, long max) {
    if (node == null) return true;
    if (node.val <= min || node.val >= max) return false;
    return isValid(node.left, min, node.val) 
           && isValid(node.right, node.val, max);
}

白话解释:给每个节点规定一个合法数值范围,检查它是否越界

2. BST中第K小的元素

解题思路

  • 中序遍历BST得到升序序列
  • 找第k个元素
java 复制代码
public int kthSmallest(TreeNode root, int k) {
    Stack<TreeNode> stack = new Stack<>();
    TreeNode curr = root;
    
    while (curr != null || !stack.isEmpty()) {
        while (curr != null) {
            stack.push(curr);
            curr = curr.left;
        }
        curr = stack.pop();
        if (--k == 0) return curr.val;
        curr = curr.right;
    }
    return -1;
}

白话解释:BST的中序遍历就是按大小排队,直接数到第k个就行

四、树的遍历花样玩法

1. 层序遍历(按层打印)

解题思路

  • 用队列实现
  • 每次处理一层
java 复制代码
public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    
    while (!queue.isEmpty()) {
        int levelSize = queue.size();
        List<Integer> currentLevel = new ArrayList<>();
        
        for (int i = 0; i < levelSize; i++) {
            TreeNode node = queue.poll();
            currentLevel.add(node.val);
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
        
        result.add(currentLevel);
    }
    return result;
}

白话解释:像点名一样,先点第一排的,记下来;再点他们的孩子,记下来...

2. Zigzag层序遍历

问题:一层从左到右,下一层从右到左

解题思路

  • 还是层序遍历
  • 加个标志位决定是否反转当前层
java 复制代码
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    boolean reverse = false;
    
    while (!queue.isEmpty()) {
        int size = queue.size();
        List<Integer> level = new ArrayList<>();
        
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll();
            level.add(node.val);
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
        
        if (reverse) Collections.reverse(level);
        result.add(level);
        reverse = !reverse;
    }
    return result;
}

白话解释:正常点名,但记名字的时候隔一行就把名单倒过来写

五、实战技巧

  1. 模板化思考:很多树题都有固定套路,掌握几个模板就能解决大部分问题
  2. 画图辅助:在纸上画出小规模的树,手动模拟算法过程
  3. 边界检查:总是考虑空树、单节点树、左斜树、右斜树等特殊情况
  4. 空间优化:有些问题可以用Morris遍历实现O(1)空间复杂度
  5. 实战练习
    • LeetCode 94:二叉树的中序遍历
    • LeetCode 101:对称二叉树
    • LeetCode 104:二叉树的最大深度
    • LeetCode 105:从前序与中序遍历序列构造二叉树
    • LeetCode 236:二叉树的最近公共祖先

记住,树题大部分都是"纸老虎",只要掌握递归思想和几种遍历方式,多加练习就能轻松应对!

相关推荐
JhonKI3 小时前
【MySQL】存储引擎 - CSV详解
android·数据库·mysql
开开心心_Every3 小时前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·windows·python·搜索引擎·智能手机·pdf·音视频
大G哥4 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师7 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork7 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly9159 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢10 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢11 小时前
Android开发报错解决
android
每次的天空13 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
每次的天空15 小时前
Android学习总结之Binder篇
android·学习·binder