力扣【二叉树专题】👊 671. 二叉树中第二小的节点

560 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 5 天,点击查看活动详情

题目链接

671. 二叉树中第二小的节点 - 力扣(LeetCode) (leetcode-cn.com)

题目描述

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。

更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。

给出这样的一个二叉树,你需要输出所有节点中的 第二小的值 。

如果第二小的值不存在的话,输出 -1 。

测试用例

示例 1:

image.png

输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5

限制

  • 树中节点数目在范围 [1, 25] 内
  • 1<=Node.val<=23111 <= Node.val <= 2^{31} - 1
  • 对于树中每个节点 root.val == min(root.left.val, root.right.val)

题目分析

抛开题目的特殊性,我们可以完整的遍历一次二叉树,记录其中全部的节点的值,存储到一个数组里,使用 Set 对数组去重后,然后为这个数组进行排序。当数组的长度为 1 时,返回 -1;当数组的长度大于 1 时, 返回数组中的第 2 小的值即可

代码实现

var findSecondMinimumValue = function(root) {
    let arr = new Set();
    trave(root);
    if (arr.size > 1) {
        arr = [...arr].sort((a, b) => a - b);
        return arr[1];
    } else {
        return -1;
    }
    function trave(node) {
        if (node == null) return null;
        arr.add(node.val);
        trave(node.left);
        trave(node.right);
    }
};

image.png

优化

上面解法中因为对数组的值进行了完整的排序,浪费了不少的性能。重写了 if 中的条件为 true 的代码块。思路是仅仅记录和替换最小的两个值,最后返回 mins[1]

let mins = [Number.MAX_VALUE,Number.MAX_VALUE];
arr.forEach(n=>{
    if(n<mins[0]){
        mins[1]=mins[0];
        mins[0]=n;
    }else if(n<mins[1]){
        mins[1]=n;
    }
})

image.png

优化2

检查代码发现,引入了不必要的数组,以及,多了一次去重操作和多了一次循环遍历,我们吧处理最小的 2 个值的操作,移到树的遍历环节,最后的代码如下:

var findSecondMinimumValue = function(root) {
    let mins = [Number.MAX_VALUE, Number.MAX_VALUE];
    trave(root);
    if (mins[1] != Number.MAX_VALUE) {
        return mins[1];
    } else {
        return -1;
    }

    function trave(node) {
        if (node == null) return null;
        let n = node.val;
        if (n < mins[0]) {
            mins[1] = mins[0];
            mins[0] = n;
        } else if (n == mins[0]) {
        } else if (n < mins[1]) {
            mins[1] = n;
        }
        trave(node.left);
        trave(node.right);
    }
};

image.png