力扣解题-530. 二叉搜索树的最小绝对差

0 阅读6分钟

力扣解题-530. 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

image.png

输入:root = [4,2,6,1,3]

输出:1

示例 2:

image.png

输入:root = [1,0,48,null,null,12,49]

输出:1

提示:

树中节点的数目范围是 [2, 10⁴]

0 <= Node.val <= 10⁵

Related Topics

树、深度优先搜索、广度优先搜索、二叉搜索树、二叉树


第一次解答(核心最优解)

解题思路

核心方法:二叉搜索树(BST)中序遍历法,利用BST“中序遍历结果为升序序列”的核心特性,只需比较升序序列中相邻节点的差值,即可找到全局最小绝对差(无需比较所有节点对),时间复杂度O(n)、空间复杂度O(h)(h为树的高度),是本题的最优解法。

核心逻辑拆解

BST的最小绝对差的核心规律:

  1. BST关键特性:中序遍历BST得到的节点值序列是严格升序的(如示例1中序遍历结果为[1,2,3,4,6]);
  2. 最小差值规律:升序序列中,最小绝对差一定出现在相邻元素之间(如[1,2,3,4,6]中,2-1=1、3-2=1、4-3=1、6-4=2,最小差值为1);
  3. 算法核心:通过中序遍历遍历BST,记录前一个节点值,实时计算当前节点与前一个节点的差值,更新最小差值。
具体执行逻辑
  1. 初始化变量
    • prev:记录中序遍历的前一个节点值,初始为null(无前置节点);
    • minDiff:记录最小绝对差,初始化为Integer.MAX_VALUE(保证首次比较一定会更新);
  2. 中序遍历递归
    • 递归终止条件:当前节点node == null,直接返回;
    • 先递归遍历左子树(中序遍历“左”);
    • 处理当前节点:若prev != null,计算当前节点值与prev的差值,更新minDiff为“当前minDiff”和“该差值”的较小值;
    • 更新prev为当前节点值(为下一个节点比较做准备);
    • 递归遍历右子树(中序遍历“右”);
  3. 返回结果:遍历完成后,minDiff即为全局最小绝对差。
执行流程可视化(以示例1 root=[4,2,6,1,3]为例)
遍历节点prev值差值计算minDiff更新最终prev
1nullMAX_VALUE1
212-1=112
323-2=113
434-3=114
646-4=216
最终返回minDiff=1,符合示例1结果。
关键细节说明
  • 差值无需取绝对值:因中序遍历是升序序列,node.val >= prev,差值node.val - prev天然为正,无需额外调用Math.abs()
  • 变量作用域prevminDiff定义为类成员变量(或方法内局部变量+数组封装),保证递归过程中值的连续性;
  • 空间优化:无需存储完整的中序遍历序列,仅需记录前一个节点值,空间复杂度从O(n)降至O(h);
  • 边界处理:题目保证节点数≥2,无需处理只有单个节点的场景。
性能说明
  • 时间复杂度:O(n)(每个节点仅被访问一次,中序遍历的线性复杂度);
  • 空间复杂度:O(h)(递归栈深度等于树的高度,平衡BST为O(logn),斜树为O(n));
  • 优势:
    1. 利用BST特性,将“比较所有节点对”的O(n²)复杂度降至O(n);
    2. 空间效率最优,无需额外存储完整序列;
    3. 递归逻辑简洁,贴合BST中序遍历的经典范式。
private Integer prev = null;          // 记录中序遍历的前一个节点值
private int minDiff = Integer.MAX_VALUE; // 初始化为最大整数
public int getMinimumDifference(TreeNode root) {
    inorder(root);
    return minDiff;
}

public void inorder(TreeNode node) {
    if (node == null) {
        return;
    }
    inorder(node.left);
    if (prev != null) {
        minDiff=Math.min(minDiff,node.val-prev);
    }
    prev=node.val;
    inorder(node.right);
}

示例解答

解题思路

解法1:中序遍历迭代法(非递归,避免栈溢出)

核心方法:使用栈模拟中序遍历的递归过程,同样记录前一个节点值并计算相邻差值,逻辑与递归法一致,避免递归栈溢出风险(如斜树场景)。

代码实现
public int getMinimumDifference(TreeNode root) {
    int minDiff = Integer.MAX_VALUE;
    Integer prev = null;
    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 (prev != null) {
            minDiff = Math.min(minDiff, curr.val - prev);
        }
        prev = curr.val;
        // 遍历右子树
        curr = curr.right;
    }
    return minDiff;
}
核心逻辑说明
  1. 栈模拟递归:通过栈存储待处理的节点,先遍历到左子树最深处,再弹出节点处理,最后遍历右子树;
  2. 状态保持prevminDiff为局部变量,无需类成员变量,代码封装性更好;
  3. 终止条件curr == null且栈为空时,遍历结束。
性能说明
  • 时间复杂度:O(n)(与递归法一致);
  • 空间复杂度:O(h)(栈深度等于树的高度);
  • 优势:
    1. 非递归实现,避免极端斜树导致的递归栈溢出;
    2. 变量为局部变量,无线程安全问题;
  • 劣势:代码量略多于递归法,需记忆中序遍历迭代模板。
解法2:存储完整中序序列法(直观但空间稍高)

核心方法:先通过中序遍历将BST节点值存入列表(升序),再遍历列表计算相邻元素的最小差值,逻辑最直观,适合新手理解。

代码实现
public int getMinimumDifference(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    // 中序遍历收集所有节点值
    inorderCollect(root, list);
    
    int minDiff = Integer.MAX_VALUE;
    // 遍历升序列表,计算相邻差值
    for (int i = 1; i < list.size(); i++) {
        minDiff = Math.min(minDiff, list.get(i) - list.get(i-1));
    }
    return minDiff;
}

private void inorderCollect(TreeNode node, List<Integer> list) {
    if (node == null) {
        return;
    }
    inorderCollect(node.left, list);
    list.add(node.val);
    inorderCollect(node.right, list);
}
核心逻辑说明
  1. 两步操作:先收集所有节点值到升序列表,再遍历列表找最小相邻差值;
  2. 直观易懂:无需理解“相邻差值即最小差值”的推导,仅需知道BST中序遍历是升序;
  3. 兼容性强:即使不是BST,也可通过排序后找最小差值(但本题利用BST特性可优化)。
性能说明
  • 时间复杂度:O(n)(中序遍历O(n) + 列表遍历O(n));
  • 空间复杂度:O(n)(需存储所有节点值);
  • 优势:
    1. 逻辑最直观,新手易理解;
    2. 代码分步清晰,调试方便;
  • 劣势:空间复杂度高于前两种解法,需额外存储完整序列。

总结

  1. 中序遍历递归法(核心解):O(n)时间+O(h)空间,利用BST特性实现最优解,代码简洁高效;
  2. 中序遍历迭代法:O(n)时间+O(h)空间,非递归实现,避免栈溢出,适合高树高场景;
  3. 存储完整序列法:O(n)时间+O(n)空间,逻辑直观,适合新手理解;
  4. 关键技巧
    • 核心思想:BST中序遍历为升序序列,最小绝对差必出现在相邻节点之间;
    • 性能优化:无需存储完整序列,仅记录前一个节点值即可将空间复杂度从O(n)降至O(h);
    • 差值简化:升序序列中相邻差值天然为正,无需取绝对值。