Recursive Methods on a Binary Tree
A binary tree is built from Node objects. Each node holds a value and references to its left and right subtrees. A null reference is the empty tree:
public class Node<T> {
public T value;
public Node<T> left;
public Node<T> right;
public Node(T value) {
this.value = value;
}
}
Each method below is static, takes the root of a tree, and may be called on the empty tree. The height of a tree is the number of edges on the longest path from the root down to a leaf. The depth of a node is the number of edges on the path from the root down to that node.
Solution
public static <T> int size(Node<T> node) {
if (node == null) {
return 0;
}
return 1 + size(node.left) + size(node.right);
}
Solution
public static <T> int height(Node<T> node) {
if (node == null) {
return -1;
}
return 1 + Math.max(height(node.left), height(node.right));
}
The empty tree returns -1. A leaf has height 0, and both of its subtrees are empty, so 1 + max(height(empty), height(empty)) must equal 0. That forces height(empty) to be -1.
Solution
public static <T> int countLeaves(Node<T> node) {
if (node == null) {
return 0;
}
if (node.left == null && node.right == null) {
return 1;
}
return countLeaves(node.left) + countLeaves(node.right);
}
Solution
Two leaves can have different depths, but both have the same two empty subtrees. A method that works only from the subtrees gives both leaves the same answer. Depth depends on the path above the node, not on what is below it.
Solution
public static <T> int depth(Node<T> node, Node<T> target) {
if (node == null) {
return -1;
}
if (node == target) {
return 0;
}
int left = depth(node.left, target);
if (left != -1) {
return 1 + left;
}
int right = depth(node.right, target);
if (right != -1) {
return 1 + right;
}
return -1;
}
Here is a binary tree:
╭───╮
│ A │
╰─┬─╯
┌────┴────┐
╭─┴─╮ ╭─┴─╮
│ B │ │ E │
╰─┬─╯ ╰───╯
┌──┴──┐
╭─┴─╮ ╭─┴─╮
│ C │ │ D │
╰───╯ ╰───╯
Solution
It returns 2, but the depth of E is 1. The leaves C and D do not contain the target, yet each returns 1 + max(-1, -1) = 0, which means “found here.” B then returns 1 + max(0, 0) = 1, and the root returns 1 + max(1, 0) = 2. The 1 + should apply only when a subtree found the target. A -1 has to be passed up unchanged.