Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Build confidence with an intuition-first walkthrough focused on tree fundamentals.
Given a non-empty special binary tree consisting of nodes with the non-negative value, where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, then this node's value is the smaller value among its two sub-nodes. More formally, the property root.val = min(root.left.val, root.right.val) always holds.
Given such a binary tree, you need to output the second minimum value in the set made of all the nodes' value in the whole tree.
If no such second minimum value exists, output -1 instead.
Example 1:
Input: root = [2,2,5,null,null,5,7] Output: 5 Explanation: The smallest value is 2, the second smallest value is 5.
Example 2:
Input: root = [2,2,2] Output: -1 Explanation: The smallest value is 2, but there isn't any second smallest value.
Constraints:
[1, 25].1 <= Node.val <= 231 - 1root.val == min(root.left.val, root.right.val) for each internal node of the tree.Problem summary: Given a non-empty special binary tree consisting of nodes with the non-negative value, where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, then this node's value is the smaller value among its two sub-nodes. More formally, the property root.val = min(root.left.val, root.right.val) always holds. Given such a binary tree, you need to output the second minimum value in the set made of all the nodes' value in the whole tree. If no such second minimum value exists, output -1 instead.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree
[2,2,5,null,null,5,7]
[2,2,2]
kth-smallest-element-in-a-bst)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int ans = -1;
public int findSecondMinimumValue(TreeNode root) {
dfs(root, root.val);
return ans;
}
private void dfs(TreeNode root, int val) {
if (root != null) {
dfs(root.left, val);
dfs(root.right, val);
if (root.val > val) {
ans = ans == -1 ? root.val : Math.min(ans, root.val);
}
}
}
}
// Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func findSecondMinimumValue(root *TreeNode) int {
ans, v := -1, root.Val
var dfs func(*TreeNode)
dfs = func(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
dfs(root.Right)
if root.Val > v {
if ans == -1 || ans > root.Val {
ans = root.Val
}
}
}
dfs(root)
return ans
}
# Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def findSecondMinimumValue(self, root: Optional[TreeNode]) -> int:
def dfs(root):
if root:
dfs(root.left)
dfs(root.right)
nonlocal ans, v
if root.val > v:
ans = root.val if ans == -1 else min(ans, root.val)
ans, v = -1, root.val
dfs(root)
return ans
// Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
struct Solution;
use rustgym_util::*;
trait SecondMinimum {
fn find_second_minimum_value(&self, min: &mut Option<i32>) -> Option<i32>;
fn option_min(a: Option<i32>, b: Option<i32>) -> Option<i32>;
}
impl SecondMinimum for TreeLink {
fn find_second_minimum_value(&self, min: &mut Option<i32>) -> Option<i32> {
if let Some(node) = self {
let node = node.borrow();
let left = &node.left;
let right = &node.right;
if min.is_none() {
*min = Some(node.val);
}
if min.unwrap() == node.val {
let min_left = Self::find_second_minimum_value(left, min);
let min_right = Self::find_second_minimum_value(right, min);
Self::option_min(min_left, min_right)
} else {
Some(node.val)
}
} else {
None
}
}
fn option_min(a: Option<i32>, b: Option<i32>) -> Option<i32> {
match (a, b) {
(Some(a), Some(b)) => Some(i32::min(a, b)),
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
(None, None) => None,
}
}
}
impl Solution {
fn find_second_minimum_value(root: TreeLink) -> i32 {
let mut min = None;
if let Some(second_min) = root.find_second_minimum_value(&mut min) {
second_min
} else {
-1
}
}
}
#[test]
fn test() {
let root = tree!(2, tree!(2), tree!(5, tree!(5), tree!(7)));
assert_eq!(Solution::find_second_minimum_value(root), 5);
let root = tree!(2, tree!(2), tree!(2));
assert_eq!(Solution::find_second_minimum_value(root), -1);
}
// Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #671: Second Minimum Node In a Binary Tree
// /**
// * Definition for a binary tree node.
// * public class TreeNode {
// * int val;
// * TreeNode left;
// * TreeNode right;
// * TreeNode() {}
// * TreeNode(int val) { this.val = val; }
// * TreeNode(int val, TreeNode left, TreeNode right) {
// * this.val = val;
// * this.left = left;
// * this.right = right;
// * }
// * }
// */
// class Solution {
// private int ans = -1;
//
// public int findSecondMinimumValue(TreeNode root) {
// dfs(root, root.val);
// return ans;
// }
//
// private void dfs(TreeNode root, int val) {
// if (root != null) {
// dfs(root.left, val);
// dfs(root.right, val);
// if (root.val > val) {
// ans = ans == -1 ? root.val : Math.min(ans, root.val);
// }
// }
// }
// }
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.