The problem
Given a binary tree root
, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X.
Return the number of good nodes in the binary tree.
Examples
Input: root = [3,1,4,3,null,1,5]
Output: 4
Explanation: Nodes in blue are good.
Root Node (3) is always a good node.
Node 4 -> (3,4) is the maximum value in the path starting from the root.
Node 5 -> (3,4,5) is the maximum value in the path
Node 3 -> (3,1,3) is the maximum value in the path.
Input: root = [3,3,null,4,2]
Output: 3
Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it.
Input: root = [1]
Output: 1
Explanation: Root is considered as good.
Constraints
- The number of nodes in the binary tree is in the range [1, 10^5].
- Each node’s value is between [-10^4, 10^4].
Explanation
Before we jump into code, let’s figure out what a good node means.
The node is defined as good if, from the path from the root
node all the way down to the current node, there are no nodes in this path that have a greater value than the node that we are currently at.
For example, if we look at the path from the root
and the node with value 4
:
- node with value
4
counts as a good node, because its value is greater than the value from theroot
node4 > 3
Now, let’s look from the root
node at the node on the left side with value 1
- node with value
1
does not count as a good node because its value is less than theroot
value1 < 3
Depth First Search (Preorder) Solution
func goodNodes(_ root: TreeNode?) -> Int {
func dfs(_ node: TreeNode?, _ maxVal: Int) -> Int {
guard let node = node else {
return 0
}
var maxVal = maxVal
var res = node.val >= maxVal ? 1 : 0
maxVal = max(maxVal, node.val)
res += dfs(node.left, maxVal)
res += dfs(node.right, maxVal)
return res
}
return dfs(root, root?.val ?? 0)
}
Explanation
To solve this problem we will be using DFS preorder algorithm. We are going to start from the root
node, after that we are going to visit the left subtree
, next we are going to visit the right subtree
.
To count how many good nodes we have in the left subtree
, we are going to pass the max
value that we have seen so far, and check if the current node value is greater than our max
value, and if it is, then it’s a good
node.
- When we are at the node with value
1
, we will passmax = 3
, and check if it’s agood
node - Next, we are going to update our
max
if necessary; in our case, ourmax
will be the same, because1 < 3
- Next, we are going to pass our
max
to the next node with value3
, and check if it’s agood
node - Next, we are going to apply the same algorithm to the
right subtree
, and in the end, we havefour
good nodes.
Time/ Space complexity
- Time complexity: O(n)
- Space complexity: O(n)
Breadth First Search Solution
func goodNodes(_ root: TreeNode?) -> Int {
var res = 0
var q: [(TreeNode?, Int)] = []
q.append((root, Int.min))
while !q.isEmpty {
let pair = q.removeFirst()
let node = pair.0
let maxVal = pair.1
if node!.val >= maxVal {
res += 1
}
if node?.left != nil {
q.append((node?.left, max(maxVal, node!.val)))
}
if node?.right != nil {
q.append((node?.right, max(maxVal, node!.val)))
}
}
return res
}
Time/ Space complexity
- Time complexity: O(n)
- Space complexity: O(n)