Heap · 13 of 18
Check if a Binary Tree is Heap
Check that a tree has no holes when filled row by row, and that every parent is at least as big as its children.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(h)
The problem, simply
You get a binary tree made of real nodes with left and right links. Say if it is a max heap. Two things must both be true:
- Shape: the tree is complete. Every row is full except maybe the last, and the last row is filled from the left with no gaps.
- Order: every parent is greater than or equal to each of its children.
A story to remember it
Picture it
A valid max heap. The little notes are seat numbers: a node at seat i has children at seats 2i + 1 and 2i + 2. Six nodes use seats 0 to 5 with no gaps.
Examples
A valid heap
- In
[10, 9, 8, 7, 6, 5]- Out
true
No gaps, and each parent beats its children.
Order is broken
- In
[10, 5, 9, 7, 6]- Out
false
The shape is fine, but 5 is smaller than its children 7 and 6.
A hole in the last row
- In
[10, 9, 8, 7, null, 5]- Out
false
Every parent beats its children, but node 9 has no right child while node 8 has a child. The last row has a gap.
Equal values
- In
[3, 3, 3]- Out
true
A parent only needs to be greater than or equal to its children.
One node or none
- In
[42] or []- Out
true
A single node is a heap. An empty tree is usually treated as a heap too.
Way 1: the simple way
Read the tree row by row with a waiting line, a queue, exactly like level order traversal. In a complete tree, once you meet a missing child, every child after it must be missing too.
- Put the root in the line. Set
gap = false. - Take a node from the front. Look at its left child, then its right child.
- If a child is missing, set
gap = true. - If a child is there but
gapis already true, the tree has a hole. Return false. - If a child is bigger than the node, the order is broken. Return false.
- Otherwise put the child at the back of the line.
import java.util.ArrayDeque;
import java.util.Queue;
class Solution {
public boolean isHeap(TreeNode root) {
if (root == null) return true;
Queue<TreeNode> line = new ArrayDeque<>();
line.add(root);
boolean gap = false;
while (!line.isEmpty()) {
TreeNode node = line.poll();
for (TreeNode child : new TreeNode[] { node.left, node.right }) {
if (child == null) { gap = true; continue; }
if (gap) return false; // a node after a hole
if (child.val > node.val) return false; // child beats parent
line.add(child);
}
}
return true;
}
}How long it takes: every node joins and leaves the line once, O(n). Extra room: the line holds a whole row, up to about n / 2 nodes, O(n).
Way 2: the clever way
Use the cinema seats. A tree with n nodes is complete exactly when its nodes use seats 0 to n - 1, with seat numbers given the same way an array heap stores them.
- Count the nodes,
n. - Walk the tree with recursion, carrying the seat number. The root gets seat 0. The left child of seat
igets2i + 1, the right child gets2i + 2. - If any node gets a seat
>= n, there is a hole. Return false. - If any child is bigger than its parent, return false.
Check [10, 9, 8, 7, null, 5], which has a hole
01 / 07
Count the nodes first. There are 5, so a complete tree would use seats 0 to 4.
- n
- 5
- looking now
- checking
- done
- rejected
- path
class Solution {
public boolean isHeap(TreeNode root) {
int n = count(root);
return check(root, 0, n);
}
private int count(TreeNode node) {
if (node == null) return 0;
return 1 + count(node.left) + count(node.right);
}
private boolean check(TreeNode node, int seat, int n) {
if (node == null) return true;
if (seat >= n) return false; // a hole before this node
if (node.left != null && node.left.val > node.val) return false;
if (node.right != null && node.right.val > node.val) return false;
return check(node.left, 2 * seat + 1, n)
&& check(node.right, 2 * seat + 2, n);
}
}How long it takes: one walk to count and one walk to check, O(n). Extra room: the recursion goes as deep as the tree, O(h). For a real heap h is about log n. For a long thin tree the counting walk can go n deep, but the checking walk stops as soon as a seat number reaches n.
Watch out for
- A tree that leans left, like
[3, 2, null, 1], has the right order but is not complete. Node 1 gets seat 3 while only seats 0 to 2 exist. - Seat numbers grow like
2^depth. On a very deep thin tree they overflowint. In Way 2 the walk returns false long before that, as soon as a seat passesn. If you compute seats any other way, uselong. - For a min heap, flip the two comparisons.
- An empty tree: decide with the interviewer. Most judges say true.
What interviewers ask next
- Check a min heap instead? Flip
>to<in the two child checks. - Given an array instead of a tree? The shape is always complete, so only check
a[i] >= a[2i + 1]anda[i] >= a[2i + 2]for every parenti < n / 2. - Check only that the tree is complete (LeetCode 958)? Use either way without the value checks.
- Can you do it in one pass? Yes, with the queue way, or with a post-order walk that returns the height and whether each subtree is perfect or complete. Be careful: a check like "left count is between right count and twice it plus one" is not enough to prove completeness.
Remember this
Grown-up words: the seats are array indexes of a binary heap, no holes means a complete binary tree, parent beats child is the max heap property, the waiting line is a queue for breadth first search, and the walk with seat numbers is a depth first search.