题目:给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
示例:
解释:
最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。
提示:
树中至少有 2 个节点。
本题与 783 二叉搜索树节点最小距离 相同
class Solution {
int answer=Integer.MAX_VALUE;
TreeNode mark=null;
public int getMinimumDifference(TreeNode root) {
dfs(root);
return answer;
}
public void dfs(TreeNode root){
if (root==null) {
return;
}
dfs(root.left);
if(mark!=null){
answer=Math.min(answer,Math.abs(mark.val-root.val));
}
mark=root;
dfs(root.right);
}
}
//官方答案如下:
class Solution {
int pre;
int ans;
public int getMinimumDifference(TreeNode root) {
ans = Integer.MAX_VALUE;
pre = -1;
dfs(root);
return ans;
}
public void dfs(TreeNode root) {
if (root == null) {
return;
}
dfs(root.left);
if (pre == -1) {
pre = root.val;
} else {
ans = Math.min(ans, root.val - pre);//动态更新最小绝对差
pre = root.val;
}
dfs(root.right);
}
}
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
解析:本题要求二叉搜索树任意俩节点的绝对差最小,那么我们可以从二叉搜索树的结构特点上得出,二叉搜索树的中序遍历得出的是递增有序的。
&emsp:&emsp:所以本题需要用到中序遍历算法来解决问题。朴素的方法是经过一次中序遍历将值保存在一个数组中再进行遍历求解,我们也可以在中序遍历的过程中用pre 变量保存前驱节点的值,这样即能边遍历边更新答案,不再需要显式创建数组来保存,需要注意的是pre 的初始值需要设置成任意负数标记开头,上文代码(官方)中设置为 -1。
二叉树的中序遍历有多种方法:可以是递归、栈、Mirrors方法
复杂度分析
时间复杂度:O(n)O(n),其中 nn 为二叉搜索树节点的个数。每个节点在中序遍历中都会被访问一次且只会被访问一次,因此总时间复杂度为 O(n)O(n)。
空间复杂度:O(n)O(n)。递归函数的空间复杂度取决于递归的栈深度,而栈深度在二叉搜索树为一条链的情况下会达到 O(n)O(n) 级别。
第一次听到Mirrors方法中序遍历,百度了下,原来Mirrors方法就是非递归、非栈的遍历方式,下面就是对Mirrors终须遍历的示例:
void inorderMorrisTraversal(TreeNode *root) {
TreeNode *cur = root, *prev = NULL;
while (cur != NULL)
{
if (cur->left == NULL) // 1.
{
printf("%d ", cur->val);
cur = cur->right;
}
else
{
// find predecessor
prev = cur->left;
while (prev->right != NULL && prev->right != cur)
prev = prev->right;
if (prev->right == NULL) // 2.a)
{
prev->right = cur;
cur = cur->left;
}
else // 2.b)
{
prev->right = NULL;
printf("%d ", cur->val);
cur = cur->right;
}
}
}
}
复杂度分析:
空间复杂度:O(1),因为只用了两个辅助指针。
时间复杂度:O(n)。证明时间复杂度为O(n),最大的疑惑在于寻找中序遍历下二叉树中所有节点的前驱节点的时间复杂度是多少,即以下两行代码:
while (prev->right != NULL && prev->right != cur){
prev = prev->right;}
直觉上,认为它的复杂度是O(nlgn),因为找单个节点的前驱节点与树的高度有关。但事实上,寻找所有节点的前驱节点只需要O(n)时间。n个节点的二叉树中一共有n-1条边,整个过程中每条边最多只走2次,一次是为了定位到某个节点,另一次是为了寻找上面某个节点的前驱节点,如下图所示,其中红色是为了定位到某个节点,黑色线是为了找到前驱节点。所以复杂度为O(n)。