Height and Depth of a Binary Tree Explained — With Code and Visuals
Binary trees show up everywhere in software — from how your operating system organises files, to how databases run lightning-fast searches, to how compilers parse your code. Before you can work with any of those systems, you need to be able to answer two deceptively simple questions: 'How deep is this node?' and 'How tall is this tree?' Those two questions have precise answers, and knowing them unlocks a huge chunk of tree-related problem solving.
The confusion beginners run into is that height and depth sound like the same thing — and in everyday English they almost are. But in binary trees they measure opposite directions. Depth is measured top-down (root toward leaves). Height is measured bottom-up (leaves toward root). Mix them up and your algorithms silently produce wrong answers, which is one of the most frustrating kinds of bug to track down.
By the end of this article you'll be able to define height and depth precisely, calculate both by hand on any tree you draw, write a clean recursive Java implementation from memory, and explain the difference confidently in an interview. We'll build everything from zero — no prior tree knowledge assumed.
What Is a Binary Tree? (Building the Foundation)
Before we measure anything, let's make sure we agree on what we're measuring.
A binary tree is a collection of nodes where each node stores a value and can have at most two children — a left child and a right child. The topmost node (with no parent) is called the root. Nodes with no children at all are called leaf nodes.
Think of it like a tournament bracket. There's one champion at the top (the root). That champion beat two finalists (left and right children). Each finalist beat two semi-finalists, and so on, all the way down to the first-round players who never won a match (the leaves).
Here's the tree we'll use throughout this entire article. Keep this picture in your head:
`` 10 <- Root (Level 0) / \ 5 20 <- Level 1 / \ \ 3 7 25 <- Level 2 / 1 <- Level 3 (deepest leaf) ``
Every measurement we make in the following sections uses exactly this tree. No surprises.
// A single node in a binary tree. // Each node holds a value and optional references to a left and right child. public class BinaryTreeNode { int value; // The data stored at this node BinaryTreeNode leftChild; // Reference to the left subtree (null if none) BinaryTreeNode rightChild; // Reference to the right subtree (null if none) // Constructor: create a new node with a value and no children yet public BinaryTreeNode(int value) { this.value = value; this.leftChild = null; this.rightChild = null; } // Helper method so we can print a node nicely (useful for debugging) @Override public String toString() { return "Node(" + value + ")"; } // --- Build the example tree from the article --- public static BinaryTreeNode buildExampleTree() { // 10 // / \ // 5 20 // / \ \ // 3 7 25 // / // 1 BinaryTreeNode root = new BinaryTreeNode(10); BinaryTreeNode node5 = new BinaryTreeNode(5); BinaryTreeNode node20 = new BinaryTreeNode(20); BinaryTreeNode node3 = new BinaryTreeNode(3); BinaryTreeNode node7 = new BinaryTreeNode(7); BinaryTreeNode node25 = new BinaryTreeNode(25); BinaryTreeNode node1 = new BinaryTreeNode(1); root.leftChild = node5; // 10's left child is 5 root.rightChild = node20; // 10's right child is 20 node5.leftChild = node3; // 5's left child is 3 node5.rightChild = node7; // 5's right child is 7 node20.rightChild = node25; // 20's right child is 25 node3.leftChild = node1; // 3's left child is 1 return root; // hand back the top of the tree } public static void main(String[] args) { BinaryTreeNode root = buildExampleTree(); System.out.println("Root node: " + root); System.out.println("Root's left child: " + root.leftChild); System.out.println("Root's right child: " + root.rightChild); System.out.println("Deepest leaf: " + root.leftChild.leftChild.leftChild); } }
Root's left child: Node(5)
Root's right child: Node(20)
Deepest leaf: Node(1)
Depth of a Node — How Far Down Are You?
The depth of a node is the number of edges (connections) you travel from the root to reach that node.
The root itself has depth 0 — you're already there, no travel needed. Its direct children have depth 1. Their children have depth 2, and so on.
Using our example tree: - Node 10 (root): depth 0 - Node 5, Node 20: depth 1 - Node 3, Node 7, Node 25: depth 2 - Node 1: depth 3
The rule is beautifully simple: depth(node) = depth(parent) + 1.
To calculate depth in code, you walk down from the root, counting each step until you find the target node. This is naturally a recursive or iterative search — you don't know the depth until you've found the node.
One important mental model: depth is a property of a specific node relative to the root. Two nodes at the same level always have the same depth. You can't talk about 'the depth of the tree' — that would be the height (coming up next).
public class NodeDepthFinder { // Finds the depth of the node containing 'targetValue' in the tree. // 'currentNode' — the node we are currently visiting // 'targetValue' — the value we are searching for // 'currentDepth' — how many edges we have already travelled from the root // // Returns -1 if the value is not found in the tree. public static int findDepth(BinaryTreeNode currentNode, int targetValue, int currentDepth) { // Base case 1: we fell off the edge of the tree — node doesn't exist if (currentNode == null) { return -1; // signal that we couldn't find the target } // Base case 2: we found the target node! if (currentNode.value == targetValue) { return currentDepth; // the depth is however far we've already travelled } // Recursive case: search the left subtree first int depthInLeftSubtree = findDepth(currentNode.leftChild, targetValue, currentDepth + 1); // If left subtree found it, no need to check right subtree if (depthInLeftSubtree != -1) { return depthInLeftSubtree; } // Otherwise, search the right subtree return findDepth(currentNode.rightChild, targetValue, currentDepth + 1); } public static void main(String[] args) { BinaryTreeNode root = BinaryTreeNode.buildExampleTree(); // Test every node in our example tree int[] valuesToCheck = {10, 5, 20, 3, 7, 25, 1}; System.out.println("Node Value | Depth"); System.out.println("-----------|------"); for (int value : valuesToCheck) { // Always start the search from the root at depth 0 int depth = findDepth(root, value, 0); System.out.printf(" %2d | %d%n", value, depth); } // What happens when we search for a value that isn't in the tree? int missingDepth = findDepth(root, 99, 0); System.out.println("\nDepth of missing node (99): " + missingDepth); } }
-----------|------
10 | 0
5 | 1
20 | 1
3 | 2
7 | 2
25 | 2
1 | 3
Depth of missing node (99): -1
Height of a Node and Tree — How Tall Are You?
Height flips the direction. Where depth asks 'how far from the top?', height asks 'how far to the bottom?'
The height of a node is the number of edges on the longest path from that node down to any leaf below it.
Leaf nodes have height 0 — there are no edges below them, nowhere left to go. Their parents have height 1. The root's height equals the height of the entire tree.
Using our example tree: - Node 1 (leaf): height 0 - Node 3 (parent of 1): height 1 - Node 7, Node 25 (leaves): height 0 - Node 5 (path: 5→3→1 = 2 edges): height 2 - Node 20 (path: 20→25 = 1 edge): height 1 - Node 10 (root, path: 10→5→3→1 = 3 edges): height 3
The recursive formula is elegant: height(node) = 1 + max(height(leftChild), height(rightChild)).
You pick the taller of the two subtrees and add 1 for the edge connecting you to it. Recursion bottoms out at null nodes, which we define as having height -1 (so a single leaf correctly gets height 0).
public class TreeHeightCalculator { // Calculates the height of any given node. // Height = number of edges on the LONGEST path from this node to a leaf below it. // // Key insight: a null node has height -1 by convention. // This makes a single leaf node correctly return height 0: // height(leaf) = 1 + max(-1, -1) = 1 + (-1) = 0 ✓ public static int calculateHeight(BinaryTreeNode node) { // Base case: null means we've gone past a leaf — define as -1 if (node == null) { return -1; } // Recursively find the height of the left subtree int leftSubtreeHeight = calculateHeight(node.leftChild); // Recursively find the height of the right subtree int rightSubtreeHeight = calculateHeight(node.rightChild); // This node's height = 1 edge to the taller child + that child's height int tallestChildHeight = Math.max(leftSubtreeHeight, rightSubtreeHeight); return 1 + tallestChildHeight; } public static void main(String[] args) { BinaryTreeNode root = BinaryTreeNode.buildExampleTree(); // Height of the whole tree (measured from root) int treeHeight = calculateHeight(root); System.out.println("Height of the entire tree: " + treeHeight); // Individual node heights System.out.println("\nNode Value | Height"); System.out.println("-----------|-------"); System.out.printf(" %2d | %d (root — tallest point)%n", root.value, calculateHeight(root)); System.out.printf(" %2d | %d (has subtree of depth 2)%n", root.leftChild.value, calculateHeight(root.leftChild)); System.out.printf(" %2d | %d (only one child below it)%n",root.rightChild.value, calculateHeight(root.rightChild)); System.out.printf(" %2d | %d (one leaf child below it)%n",root.leftChild.leftChild.value, calculateHeight(root.leftChild.leftChild)); System.out.printf(" %2d | %d (leaf — no children)%n", root.leftChild.leftChild.leftChild.value, calculateHeight(root.leftChild.leftChild.leftChild)); System.out.printf(" %2d | %d (leaf — no children)%n", root.leftChild.rightChild.value, calculateHeight(root.leftChild.rightChild)); System.out.printf(" %2d | %d (leaf — no children)%n", root.rightChild.rightChild.value, calculateHeight(root.rightChild.rightChild)); // Edge case: what is the height of an empty tree? int emptyTreeHeight = calculateHeight(null); System.out.println("\nHeight of an empty tree (null root): " + emptyTreeHeight); } }
Node Value | Height
-----------|-------
10 | 3 (root — tallest point)
5 | 2 (has subtree of depth 2)
20 | 1 (only one child below it)
3 | 1 (one leaf child below it)
1 | 0 (leaf — no children)
7 | 0 (leaf — no children)
25 | 0 (leaf — no children)
Height of an empty tree (null root): -1
Putting It All Together — One Complete Program
Now let's combine everything into a single runnable file that builds the tree, calculates both depth and height for every node, and prints a clean summary table.
This is the kind of code you'd actually write to verify your understanding before an interview. Running it and seeing the correct numbers builds real confidence — far more than just reading about it.
Notice the relationship between depth and height: for the root, depth is 0 and height is 3. For the deepest leaf (node 1), depth is 3 and height is 0. They're perfectly complementary — they always sum to the height of the tree for nodes on the longest path. That's a neat property worth remembering.
Also notice the time complexity: calculating height for a single node costs O(n) because you visit every node in its subtree. Calculating the depth of a single node costs O(d) where d is its depth — much cheaper if the tree is balanced.
// Complete, self-contained demo. // Builds the example tree and prints a full depth + height table. public class HeightAndDepthDemo { // ── Node Definition ─────────────────────────────────────────── static class TreeNode { int value; TreeNode left, right; TreeNode(int value) { this.value = value; } } // ── Tree Builder ─────────────────────────────────────────────── // 10 // / \ // 5 20 // / \ \ // 3 7 25 // / // 1 static TreeNode buildTree() { TreeNode root = new TreeNode(10); root.left = new TreeNode(5); root.right = new TreeNode(20); root.left.left = new TreeNode(3); root.left.right = new TreeNode(7); root.right.right= new TreeNode(25); root.left.left.left = new TreeNode(1); return root; } // ── Height Calculation ───────────────────────────────────────── // Returns -1 for null (so a leaf correctly gets height 0) static int height(TreeNode node) { if (node == null) return -1; return 1 + Math.max(height(node.left), height(node.right)); } // ── Depth Calculation ────────────────────────────────────────── // Returns -1 if targetValue is not found anywhere in the tree static int depth(TreeNode node, int targetValue, int currentDepth) { if (node == null) return -1; // not found if (node.value == targetValue) return currentDepth; // found it! int leftResult = depth(node.left, targetValue, currentDepth + 1); if (leftResult != -1) return leftResult; // found in left subtree return depth(node.right, targetValue, currentDepth + 1); // try right } // ── Main ─────────────────────────────────────────────────────── public static void main(String[] args) { TreeNode root = buildTree(); int treeHeight = height(root); System.out.println("=== Binary Tree: Height and Depth Summary ==="); System.out.println("Overall tree height: " + treeHeight + " edges"); System.out.println(); System.out.printf("%-12s %-8s %-8s %-10s%n", "Node Value", "Depth", "Height", "Depth+Height"); System.out.println("----------------------------------------------"); // All node values in our tree int[] allValues = {10, 5, 20, 3, 7, 25, 1}; for (int val : allValues) { int d = depth(root, val, 0); // depth from root int h = height(findNode(root, val)); // height from this node down System.out.printf("%-12d %-8d %-8d %-10d%n", val, d, h, d + h); } System.out.println("----------------------------------------------"); System.out.println("Notice: depth + height = " + treeHeight + " for all nodes on the longest path."); } // Helper: returns the TreeNode with the given value (simple DFS search) static TreeNode findNode(TreeNode node, int targetValue) { if (node == null) return null; if (node.value == targetValue) return node; TreeNode leftResult = findNode(node.left, targetValue); return (leftResult != null) ? leftResult : findNode(node.right, targetValue); } }
Overall tree height: 3 edges
Node Value Depth Height Depth+Height
----------------------------------------------
10 0 3 3
5 1 2 3
20 1 1 2
3 2 1 3
7 2 0 2
25 2 0 2
1 3 0 3
----------------------------------------------
Notice: depth + height = 3 for all nodes on the longest path.
| Aspect | Depth of a Node | Height of a Node / Tree |
|---|---|---|
| What it measures | Distance from root DOWN to this node | Distance from this node DOWN to the farthest leaf |
| Direction | Top-down (root → node) | Bottom-up (node → leaf) |
| Root's value | 0 (no edges traversed) | Equals the height of the whole tree |
| Leaf node's value | Varies — depends on where the leaf sits | Always 0 (no edges below a leaf) |
| Empty tree (null) | N/A — no node to measure | -1 (by convention, so single-node tree = 0) |
| Formula | depth(node) = depth(parent) + 1 | height(node) = 1 + max(height(left), height(right)) |
| Algorithm direction | Search from root downward | Recurse to leaves, then bubble results back up |
| Time complexity | O(d) where d = depth of target node | O(n) — must visit every node in subtree |
| Used for | Level-order traversal, BFS layers | Balancing checks, tree rotation decisions |
🎯 Key Takeaways
- Depth measures top-down: it counts edges from the root to a specific node. Root always has depth 0.
- Height measures bottom-up: it counts edges on the longest path from a node down to any leaf below it. Leaf nodes always have height 0.
- Define null nodes as height -1 in your code — this is the correct base case that makes all other height values work out cleanly without special-casing leaves.
- The height of the entire tree equals the height of the root node — they are the same measurement. You don't need a separate function for 'tree height'.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Treating null as height 0 instead of -1 — This causes leaf nodes to report height 1 instead of 0, and a tree with only a root reports height 1 instead of 0. Fix it by returning -1 for null in your height function: 'if (node == null) return -1;'. Then height(leaf) = 1 + max(-1, -1) = 0, which is correct.
- ✕Mistake 2: Starting depth at 1 instead of 0 — Beginners naturally think 'the root is on level 1' because humans count from 1. This shifts every depth answer up by 1 and breaks any algorithm that relies on depth = 0 meaning 'this is the root'. Always initialise currentDepth to 0 when you start the search from the root.
- ✕Mistake 3: Confusing height of the tree with height of the root — They are the same thing, but beginners sometimes try to separately 'count levels' rather than just calling height(root). There's no need for a separate tree-height function — the height of the tree IS the height of its root node. Calling calculateHeight(root) gives you the full tree height directly.
Interview Questions on This Topic
- QWhat is the difference between the height and depth of a node in a binary tree? Can you give an example using a tree you draw on the whiteboard?
- QWrite a recursive function to find the height of a binary tree. What is its time and space complexity, and why?
- QA balanced binary tree has n nodes. What is the relationship between its height and n? (Expected answer: height is O(log n). Follow-up: what happens to height in the worst case, a skewed tree, and why?)
Frequently Asked Questions
What is the height of a binary tree with only one node?
It's 0. A single node is both the root and a leaf. There are no edges below it, so the longest path to a leaf has 0 edges. This is why we define null nodes as height -1: height(single node) = 1 + max(-1, -1) = 0.
Is the height of a tree the same as the number of levels?
Almost — but off by one. A tree with height 3 has 4 levels (levels 0, 1, 2, and 3). Height counts edges; levels count nodes on each tier. So number of levels = height + 1. This off-by-one trips people up constantly, so keep it in mind.
What is the depth of a tree versus the height of a tree?
Strictly speaking, 'depth of a tree' isn't a standard term — depth applies to individual nodes. The correct term for how tall the whole tree is is the 'height of the tree', which equals the height of the root node. Some textbooks loosely use depth of tree to mean the same as height, so if you see it in the wild, they almost certainly mean height.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.