听风是风

学或不学,知识都在那里,只增不减。

导航

JS Leetcode 530. 二叉搜索树的最小绝对差 题解分析,再次了解中序遍历

壹 ❀ 引

本题来自LeetCode 783. 二叉搜索树节点最小距离,题目描述如下:

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

示例 1:

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

示例 2:

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

提示:

树中节点数目在范围 [2, 100] 内
0 <= Node.val <= 105
差值是一个正数,其数值等于两值之差的绝对值

贰 ❀ 题解分析

也许当你看到此题,你会想着题目要求任意两个不同节点中找到最小差,应该怎么遍历才好呢。我们假定给你一个有序的数组[1,3,4,6],如果让你求最小差会怎么做?你一定会每次拿两个相邻的数进行求差,然后右移,一直找到最小的差集。

为什么是相邻的两个数?因为数组既然有序了,不找相邻难道求两个相隔N个数的差吗?比如上面的数组3-1的差一定比4-1的小。

所以暴力的做法,我们可以用递归的方式将二叉树转为数组,排序后依次遍历,并找出最小的差。虽然这样做可以,但其实没必要。

让我们再回头看一眼例子中的二叉树,你会发现每个左子树的节点,一定比父节点小,然后父节点又比右子树的节点小。

比如图1中,1<2<3,而3有可以看成是4的左子树,且2<4<5,哎?如果我们按照这个顺序去遍历,就不用生成数组还做排序了,因为这个过程就是有序的,那么这个查找过程其实就是中序遍历了。我在JS 前序遍历、中序遍历、后序遍历、层序遍历详解,深度优先与广度优先区别,附leetcode例题题解答案一文中介绍了前序遍历,中序遍历以及后续遍历的基本模板与遍历过程,有兴趣的同学可以看看。

那么我们直接贴上中序遍历的模板:

var inorderTraversal = function(root) {
    // 遍历函数
    function traversal(root) {
        if (root !== null) {
            if (root.left) {
                // 递归遍历左子树
                traversal(root.left);
            };
            // 访问根节点的值
            console.log(root.val)
            if (root.right) {
                // 递归遍历右子树
                traversal(root.right);
            };
        };
    };
    traversal(root);
};

因为中序遍历满足左节点------>根节点------>右节点的顺序,所以如果一个根节点有左节点,那就先访问左节点,而左节点可能还有自己的左节点,所以递归一直往下找,直到找不到了,这个时候我们就可以处理我们想要的逻辑了,比如打印出此时节点的值。

我们知道,函数调用栈是先进后出,当左节点的函数执行完成被释放,那自然下一个执行的就是自己的根节点的函数了,根节点函数执行完被释放,自然又会执行右节点的函数....一直执行,知道整个二叉树的节点被访问完毕。

那么我们就可以利用中序遍历的特点,在访问每个节点的时候,求跟上一个节点的差值,并存起来,然后依次比较,注意,我们要存2个东西,一个是上一个节点的值,这个值在递归时是不断被更新的,第二个是最小值,如果遇到更小就更新它即可。

综合上面的想法,让我们来实现它:

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var getMinimumDifference = function (root) {
    function findMin(node) {
        if (node === null) {
            return;
        };
        if (node.left) {
            findMin(node.left);
        };
        // 这里是为了避免第一个节点参与比较
        if (pre !== null) {
          	// abs用于消除差为负数的情况,直接比较绝对值
            min = Math.min(min, Math.abs(node.val - pre));
        };
        pre = node.val;
        if (node.right) {
            findMin(node.right);
        };
    };
    // 第一个节点因为找不到更前面的节点了,所以故意设置个null用于消除第一次比较
    let pre = null, min = Infinity;
    findMin(root);
    return min;
};

唯一需要注意的是是初始pre是null,目的是第一个左节点找不到其它节点进行求差,准确来说应该是从第二个节点开始才能求出差,所以设为null就是这个目的,其它就没什么难度了,那么本文结束。

posted on 2021-04-17 00:17  听风是风  阅读(146)  评论(0编辑  收藏  举报