题目描述
思路解析动画文字版
核心一句话:BST 中序 = 升序,最小绝对差一定来自中序相邻两数,边遍历边比相邻差。
完整的 BST。中序遍历从最左下角开始,按「左子树 → 根 → 右子树」访问,得到升序序列。我们维护一个 prev(上一个访问的值)和 minDiff(目前最小相邻差)。
中序怎么找下一个?从根一路向左压栈,走到最左下角。当前定位到节点 2(它是所有未访问节点里最小的)。下一步把它和前驱(暂无前驱)比一下相邻差。
中序第一个访问到的就是最小值 2(橙色)。它还没有前驱,先把它存进 prev,暂时不算差。
节点 2 记为前驱(蓝色=已访问)。还没有相邻差可比,继续中序走下一个更大的数。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 4(它是所有未访问节点里最小的)。下一步把它和前驱 2比一下相邻差。
中序访问到 4(橙色),它的前驱是 2(也是橙色)。两者在升序里相邻,算相邻差:|4 - 2| = 2。
相邻差 2 比之前的最小差更小!更新 minDiff = 2。节点 4 记为新的前驱(蓝色),继续往后走。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 6(它是所有未访问节点里最小的)。下一步把它和前驱 4比一下相邻差。
中序访问到 6(橙色),它的前驱是 4(也是橙色)。两者在升序里相邻,算相邻差:|6 - 4| = 2。
相邻差 2 没比当前 minDiff = 2 更小,minDiff 不变。节点 6 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 9(它是所有未访问节点里最小的)。下一步把它和前驱 6比一下相邻差。
中序访问到 9(橙色),它的前驱是 6(也是橙色)。两者在升序里相邻,算相邻差:|9 - 6| = 3。
相邻差 3 没比当前 minDiff = 2 更小,minDiff 不变。节点 9 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 11(它是所有未访问节点里最小的)。下一步把它和前驱 9比一下相邻差。
中序访问到 11(橙色),它的前驱是 9(也是橙色)。两者在升序里相邻,算相邻差:|11 - 9| = 2。
相邻差 2 没比当前 minDiff = 2 更小,minDiff 不变。节点 11 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 13(它是所有未访问节点里最小的)。下一步把它和前驱 11比一下相邻差。
中序访问到 13(橙色),它的前驱是 11(也是橙色)。两者在升序里相邻,算相邻差:|13 - 11| = 2。
相邻差 2 没比当前 minDiff = 2 更小,minDiff 不变。节点 13 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 14(它是所有未访问节点里最小的)。下一步把它和前驱 13比一下相邻差。
中序访问到 14(橙色),它的前驱是 13(也是橙色)。两者在升序里相邻,算相邻差:|14 - 13| = 1。
相邻差 1 比之前的最小差更小!更新 minDiff = 1。节点 14 记为新的前驱(蓝色),继续往后走。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 20(它是所有未访问节点里最小的)。下一步把它和前驱 14比一下相邻差。
中序访问到 20(橙色),它的前驱是 14(也是橙色)。两者在升序里相邻,算相邻差:|20 - 14| = 6。
相邻差 6 没比当前 minDiff = 1 更小,minDiff 不变。节点 20 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 21(它是所有未访问节点里最小的)。下一步把它和前驱 20比一下相邻差。
中序访问到 21(橙色),它的前驱是 20(也是橙色)。两者在升序里相邻,算相邻差:|21 - 20| = 1。
相邻差 1 没比当前 minDiff = 1 更小,minDiff 不变。节点 21 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 22(它是所有未访问节点里最小的)。下一步把它和前驱 21比一下相邻差。
中序访问到 22(橙色),它的前驱是 21(也是橙色)。两者在升序里相邻,算相邻差:|22 - 21| = 1。
相邻差 1 没比当前 minDiff = 1 更小,minDiff 不变。节点 22 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 23(它是所有未访问节点里最小的)。下一步把它和前驱 22比一下相邻差。
中序访问到 23(橙色),它的前驱是 22(也是橙色)。两者在升序里相邻,算相邻差:|23 - 22| = 1。
相邻差 1 没比当前 minDiff = 1 更小,minDiff 不变。节点 23 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 25(它是所有未访问节点里最小的)。下一步把它和前驱 23比一下相邻差。
中序访问到 25(橙色),它的前驱是 23(也是橙色)。两者在升序里相邻,算相邻差:|25 - 23| = 2。
相邻差 2 没比当前 minDiff = 1 更小,minDiff 不变。节点 25 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?弹回栈顶的父节点(左子树已数完)。当前定位到节点 28(它是所有未访问节点里最小的)。下一步把它和前驱 25比一下相邻差。
中序访问到 28(橙色),它的前驱是 25(也是橙色)。两者在升序里相邻,算相邻差:|28 - 25| = 3。
相邻差 3 没比当前 minDiff = 1 更小,minDiff 不变。节点 28 记为前驱(蓝色),继续中序走下一个。
中序怎么找下一个?从上一个节点转向它的右子树,再一路向左到底。当前定位到节点 31(它是所有未访问节点里最小的)。下一步把它和前驱 28比一下相邻差。
中序访问到 31(橙色),它的前驱是 28(也是橙色)。两者在升序里相邻,算相邻差:|31 - 28| = 3。
相邻差 3 没比当前 minDiff = 1 更小,minDiff 不变。节点 31 记为前驱(蓝色),继续中序走下一个。
中序走完整棵树,升序序列里相邻差最小的一对是 13 和 14(绿色),|14 - 13| = 1。这就是全树任意两点的最小绝对差 —— 不必两两比较,只看升序相邻就够。
边界都落在中序相邻对上,想清楚就不会错。
两个高频追问:退化成普通树的做法、改求最大差。
参考代码
def getMinimumDifference(root): stack, cur = [], root prev = None ans = float("inf") while stack or cur: while cur: # 一路向左 stack.append(cur) cur = cur.left cur = stack.pop() # 中序访问 cur if prev is not None: ans = min(ans, cur.val - prev) prev = cur.val # 更新前驱 cur = cur.right return ans复杂度
- 时间:O(n),中序遍历每个节点恰好访问一次
- 空间:O(H),栈最多存一条从根到叶的路径,H 为树高
易错点
面试追问把动画讲成自己的话
追问如果不是 BST,是普通二叉树,求任意两点最小绝对差怎么做?
追问如果要求最大绝对差呢?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
二叉树的直径
LeetCode 543 · 简单 · 沿着 二叉树套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题