一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 5 天,点击查看活动详情。
题目链接
671. 二叉树中第二小的节点 - 力扣(LeetCode) (leetcode-cn.com)
题目描述
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。
给出这样的一个二叉树,你需要输出所有节点中的 第二小的值 。
如果第二小的值不存在的话,输出 -1 。
测试用例
示例 1:
输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5 。
限制
- 树中节点数目在范围 [1, 25] 内
- 对于树中每个节点
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);
}
};
优化
上面解法中因为对数组的值进行了完整的排序,浪费了不少的性能。重写了 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;
}
})
优化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);
}
};