LeetCode 1026: Maximum Difference Between Node and Ancestor (DFS Path Min/Max Tracking)
LeetCode 1026Binary TreeDFSToday we solve LeetCode 1026 - Maximum Difference Between Node and Ancestor.
Source: https://leetcode.com/problems/maximum-difference-between-node-and-ancestor/
English
Problem Summary
Given a binary tree, choose any node a and any descendant b of a. We need the maximum value of |a.val - b.val| over all such ancestor-descendant pairs.
Key Insight
For each root-to-current path, only two numbers matter: the minimum value and maximum value seen so far on that path. At node x, the best ancestor gap involving x is max(|x-minPath|, |x-maxPath|). So we propagate path min/max with DFS.
Brute Force and Limitations
Brute force checks every node against all descendants, which can degrade to O(n^2) on skewed trees. This is too slow for larger inputs.
Optimal Algorithm Steps (DFS with Path Range)
1) Start DFS from root carrying pathMin = root.val, pathMax = root.val.
2) At each node, update answer with max(abs(node.val-pathMin), abs(node.val-pathMax)).
3) Update path range: newMin = min(pathMin, node.val), newMax = max(pathMax, node.val).
4) Recurse into left and right children with the updated range.
5) The global maximum is the result.
Complexity Analysis
Time: O(n), each node visited once.
Space: O(h) recursion stack, where h is tree height.
Common Pitfalls
- Comparing only parent-child differences instead of ancestor-descendant differences.
- Forgetting to carry both min and max along the path.
- Updating path range after recursion rather than before passing to children.
- Returning local subtree result without taking global maximum.
Reference Implementations (Java / Go / C++ / Python / JavaScript)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int ans = 0;
public int maxAncestorDiff(TreeNode root) {
dfs(root, root.val, root.val);
return ans;
}
private void dfs(TreeNode node, int pathMin, int pathMax) {
if (node == null) return;
ans = Math.max(ans, Math.max(Math.abs(node.val - pathMin), Math.abs(node.val - pathMax)));
int newMin = Math.min(pathMin, node.val);
int newMax = Math.max(pathMax, node.val);
dfs(node.left, newMin, newMax);
dfs(node.right, newMin, newMax);
}
}/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxAncestorDiff(root *TreeNode) int {
ans := 0
var dfs func(*TreeNode, int, int)
dfs = func(node *TreeNode, pathMin int, pathMax int) {
if node == nil {
return
}
d1 := node.Val - pathMin
if d1 < 0 {
d1 = -d1
}
d2 := node.Val - pathMax
if d2 < 0 {
d2 = -d2
}
if d1 > ans {
ans = d1
}
if d2 > ans {
ans = d2
}
newMin := pathMin
if node.Val < newMin {
newMin = node.Val
}
newMax := pathMax
if node.Val > newMax {
newMax = node.Val
}
dfs(node.Left, newMin, newMax)
dfs(node.Right, newMin, newMax)
}
dfs(root, root.Val, root.Val)
return ans
}/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int ans = 0;
int maxAncestorDiff(TreeNode* root) {
dfs(root, root->val, root->val);
return ans;
}
void dfs(TreeNode* node, int pathMin, int pathMax) {
if (!node) return;
ans = max(ans, max(abs(node->val - pathMin), abs(node->val - pathMax)));
int newMin = min(pathMin, node->val);
int newMax = max(pathMax, node->val);
dfs(node->left, newMin, newMax);
dfs(node->right, newMin, newMax);
}
};# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
ans = 0
def dfs(node: Optional[TreeNode], path_min: int, path_max: int) -> None:
nonlocal ans
if not node:
return
ans = max(ans, abs(node.val - path_min), abs(node.val - path_max))
new_min = min(path_min, node.val)
new_max = max(path_max, node.val)
dfs(node.left, new_min, new_max)
dfs(node.right, new_min, new_max)
dfs(root, root.val, root.val)
return ans/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var maxAncestorDiff = function(root) {
let ans = 0;
const dfs = (node, pathMin, pathMax) => {
if (!node) return;
ans = Math.max(ans, Math.abs(node.val - pathMin), Math.abs(node.val - pathMax));
const newMin = Math.min(pathMin, node.val);
const newMax = Math.max(pathMax, node.val);
dfs(node.left, newMin, newMax);
dfs(node.right, newMin, newMax);
};
dfs(root, root.val, root.val);
return ans;
};中文
题目概述
给定一棵二叉树,任选祖先节点 a 与其后代节点 b,要求最大化 |a.val - b.val|,返回这个最大值。
核心思路
在任意“根到当前节点”的路径上,真正有用的信息只有两个:路径最小值和路径最大值。当前节点与祖先的最大差值只可能来自这两个边界,因此 DFS 过程中持续维护 pathMin/pathMax 即可。
基线解法与不足
暴力做法是对每个节点向下枚举全部后代并计算差值,最坏会到 O(n^2)(链状树)。规模稍大就容易超时。
最优算法步骤(DFS + 路径最值)
1)从根节点开始 DFS,初始化 pathMin = pathMax = root.val。
2)在节点 x 处,用 max(|x-pathMin|, |x-pathMax|) 更新答案。
3)更新路径边界:newMin = min(pathMin, x),newMax = max(pathMax, x)。
4)把更新后的边界继续传给左右子树。
5)遍历结束后,全局最大值即答案。
复杂度分析
时间复杂度:O(n)(每个节点访问一次)。
空间复杂度:O(h)(递归栈深度,h 为树高)。
常见陷阱
- 只比较父子差值,忽略“祖先-后代”跨层关系。
- 路径上传递了最小值却忘了同时传最大值。
- 先递归再更新路径边界,导致子树拿到错误状态。
- 只返回局部结果,没有维护全局最大值。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int ans = 0;
public int maxAncestorDiff(TreeNode root) {
dfs(root, root.val, root.val);
return ans;
}
private void dfs(TreeNode node, int pathMin, int pathMax) {
if (node == null) return;
ans = Math.max(ans, Math.max(Math.abs(node.val - pathMin), Math.abs(node.val - pathMax)));
int newMin = Math.min(pathMin, node.val);
int newMax = Math.max(pathMax, node.val);
dfs(node.left, newMin, newMax);
dfs(node.right, newMin, newMax);
}
}/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxAncestorDiff(root *TreeNode) int {
ans := 0
var dfs func(*TreeNode, int, int)
dfs = func(node *TreeNode, pathMin int, pathMax int) {
if node == nil {
return
}
d1 := node.Val - pathMin
if d1 < 0 {
d1 = -d1
}
d2 := node.Val - pathMax
if d2 < 0 {
d2 = -d2
}
if d1 > ans {
ans = d1
}
if d2 > ans {
ans = d2
}
newMin := pathMin
if node.Val < newMin {
newMin = node.Val
}
newMax := pathMax
if node.Val > newMax {
newMax = node.Val
}
dfs(node.Left, newMin, newMax)
dfs(node.Right, newMin, newMax)
}
dfs(root, root.Val, root.Val)
return ans
}/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int ans = 0;
int maxAncestorDiff(TreeNode* root) {
dfs(root, root->val, root->val);
return ans;
}
void dfs(TreeNode* node, int pathMin, int pathMax) {
if (!node) return;
ans = max(ans, max(abs(node->val - pathMin), abs(node->val - pathMax)));
int newMin = min(pathMin, node->val);
int newMax = max(pathMax, node->val);
dfs(node->left, newMin, newMax);
dfs(node->right, newMin, newMax);
}
};# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
ans = 0
def dfs(node: Optional[TreeNode], path_min: int, path_max: int) -> None:
nonlocal ans
if not node:
return
ans = max(ans, abs(node.val - path_min), abs(node.val - path_max))
new_min = min(path_min, node.val)
new_max = max(path_max, node.val)
dfs(node.left, new_min, new_max)
dfs(node.right, new_min, new_max)
dfs(root, root.val, root.val)
return ans/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var maxAncestorDiff = function(root) {
let ans = 0;
const dfs = (node, pathMin, pathMax) => {
if (!node) return;
ans = Math.max(ans, Math.abs(node.val - pathMin), Math.abs(node.val - pathMax));
const newMin = Math.min(pathMin, node.val);
const newMax = Math.max(pathMax, node.val);
dfs(node.left, newMin, newMax);
dfs(node.right, newMin, newMax);
};
dfs(root, root.val, root.val);
return ans;
};
Comments