题目
标题和出处
标题:结点与其祖先之间的最大差值
难度
5 级
题目描述
要求
给定二叉树的根结点 root \texttt{root} root,找出存在于不同结点 a \texttt{a} a 和 b \texttt{b} b 之间的最大值 v \texttt{v} v,其中 v = |a.val - b.val| \texttt{v = |a.val - b.val|} v = |a.val - b.val|,且 a \texttt{a} a 是 b \texttt{b} b 的祖先。
如果 a \texttt{a} a 的任何子结点是 b \texttt{b} b,或者 a \texttt{a} a 的任何子结点是 b \texttt{b} b 的祖先,那么 a \texttt{a} a 是 b \texttt{b} b 的祖先。
示例
示例 1:
输入: root = [8,3,10,1,6,null,14,null,null,4,7,13] \texttt{root = [8,3,10,1,6,null,14,null,null,4,7,13]} root = [8,3,10,1,6,null,14,null,null,4,7,13]
输出: 7 \texttt{7} 7
解释:
我们有若干结点与其祖先的差值,其中一些如下:
|8 - 3| = 5 \texttt{|8 - 3| = 5} |8 - 3| = 5
|3 - 7| = 4 \texttt{|3 - 7| = 4} |3 - 7| = 4
|8 - 1| = 7 \texttt{|8 - 1| = 7} |8 - 1| = 7
|10 - 13| = 3 \texttt{|10 - 13| = 3} |10 - 13| = 3
在所有可能的差值中,最大值 7 \texttt{7} 7 由 |8 - 1| = 7 \texttt{|8 - 1| = 7} |8 - 1| = 7 得出。
示例 2:
输入: root = [1,null,2,null,0,3] \texttt{root = [1,null,2,null,0,3]} root = [1,null,2,null,0,3]
输出: 3 \texttt{3} 3
数据范围
- 树中结点数目在范围 [2, 5000] \texttt{[2, 5000]} [2, 5000] 内
- 0 ≤ Node.val ≤ 10 5 \texttt{0} \le \texttt{Node.val} \le \texttt{10}^\texttt{5} 0≤Node.val≤105
解法一
思路和算法
二叉树中的每个结点对应一条从根结点到该结点的路径,路径存在最大结点值和最小结点值。对于每个结点,该结点与其祖先之间的最大差值可能是以下两种情况之一:
-
路径上的最大结点值与当前结点值之差;
-
当前结点值与路径上的最小结点值之差。
可以使用深度优先搜索计算二叉树中的每个结点与其祖先之间的最大差值。从根结点开始遍历全部结点,由于同一条路径上的结点的遍历顺序是从上到下,在访问到一个结点时,该结点的所有祖先结点都已经被访问过,因此可以得到从根结点到该结点的路径上的最大结点值和最小结点值,计算当前结点与其祖先之间的最大差值。
在访问一个结点之后,对于其非空子结点,更新从根结点到子结点的路径上的最大结点值和最小结点值,对子结点继续遍历。
遍历结束之后,即可得到二叉树中结点与祖先之间的最大差值。
题目要求计算差值的两个结点应满足两个结点不同且一个结点是另一个结点的祖先。上述做法虽然没有考虑计算差值的两个结点是否相同,但是也能得到正确的结果,理由如下。
由于给定的二叉树的结点数目至少为 2 2 2,因此一定存在两个不同的结点且这两个结点之间存在祖先和后代的关系。任何一个结点与其自身的差值一定是 0 0 0。对于两个不同的结点,如果结点值相同则差值等于 0 0 0,如果结点值不同则差值大于 0 0 0,即差值一定不会小于 0 0 0,因此一定存在两个不同的结点,这两个结点之间存在祖先和后代的关系且这两个结点值之差的绝对值等于最大差值。
代码
class Solution {
int maxDiff = 0;
public int maxAncestorDiff(TreeNode root) {
dfs(root, root.val, root.val);
return maxDiff;
}
public void dfs(TreeNode node, int maxValue, int minValue) {
int currDiff = Math.max(maxValue - node.val, node.val - minValue);
maxDiff = Math.max(maxDiff, currDiff);
TreeNode left = node.left, right = node.right;
if (left != null) {
int leftMaxValue = Math.max(left.val, maxValue);
int leftMinValue = Math.min(left.val, minValue);
dfs(left, leftMaxValue, leftMinValue);
}
if (right != null) {
int rightMaxValue = Math.max(right.val, maxValue);
int rightMinValue = Math.min(right.val, minValue);
dfs(right, rightMaxValue, rightMinValue);
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)。
解法二
思路和算法
也可以使用广度优先搜索计算二叉树中结点与祖先之间的最大差值。从根结点开始遍历全部结点,访问结点的顺序为层数递增的顺序,遍历过程中,对于每个结点,得到该结点对应的从根结点到该结点的路径上的最大结点值和最小结点值,计算该结点与其祖先之间的最大差值。
为了记录每个结点对应的从根结点到该结点的路径上的最大结点值和最小结点值,需要使用三个队列,分别存储结点、路径上的最大结点值和路径上的最小结点值,这三个队列分别为结点队列、最大值队列和最小值队列,初始时将根结点入结点队列,将根结点值入最大值队列和最小值队列。每次将一个结点、一个最大值和一个最小值分别从结点队列、最大值队列和最小值队列出队列,计算当前结点与其祖先之间的最大差值。对于当前结点的非空子结点,计算从根结点到子结点的路径上的最大结点值和最小结点值,然后将子结点以及子结点对应的最大结点值和最小结点值分别入结点队列、最大值队列和最小值队列。
遍历结束之后,即可得到二叉树中结点与祖先之间的最大差值。
代码
class Solution {
public int maxAncestorDiff(TreeNode root) {
int maxDiff = 0;
Queue<TreeNode> nodeQueue = new ArrayDeque<TreeNode>();
Queue<Integer> maxNode = new ArrayDeque<Integer>();
Queue<Integer> minNode = new ArrayDeque<Integer>();
nodeQueue.offer(root);
maxNode.offer(root.val);
minNode.offer(root.val);
while (!nodeQueue.isEmpty()) {
TreeNode node = nodeQueue.poll();
int maxValue = maxNode.poll();
int minValue = minNode.poll();
int currDiff = Math.max(maxValue - node.val, node.val - minValue);
maxDiff = Math.max(maxDiff, currDiff);
TreeNode left = node.left, right = node.right;
if (left != null) {
nodeQueue.offer(left);
maxNode.offer(Math.max(left.val, maxValue));
minNode.offer(Math.min(left.val, minValue));
}
if (right != null) {
nodeQueue.offer(right);
maxNode.offer(Math.max(right.val, maxValue));
minNode.offer(Math.min(right.val, minValue));
}
}
return maxDiff;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是队列空间,队列内元素个数不超过 n n n。