深入浅出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:二叉树的最近公共祖先

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

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui