Binary trees
Table of contents
Binary search
This week, we’ll be learning about the fundamental data structure behind Java’s TreeSet
and TreeMap
implementations: binary search trees (BSTs). In a binary search tree, elements are stored in sorted order. In the worst case, the runtime of binary search tree operations are all in Θ(log2 N) with respect to the N, the size of the binary search tree.
Binary search trees are very closely related to the binary search algorithm that we introduced earlier for finding the index of a target element in a sorted array. In each iteration of the binary search algorithm, half of the elements under consideration can be discarded, resulting in a logarithmic time algorithm.
Unfortunately, arrays are fixed in size, making it difficult to add new items to a sorted array-based set. Inserting elements into a sorted array will take linear time since we’ll need to copy over all of the elements into a new, sorted array. When we ran into this problem with inserting or removing elements from the beginning of an arrays, we applied a new approach based on linked nodes.
Unfortunately, this doesn’t work well for binary search.
Why is binary search in sorted a linked list so much slower than in a sorted array?
Unlike arrays, getting the middle element of a linked list is very slow since we need to iterate (or recurse) to the middle of the list. This is much, much slower than an array!
- Goal
- Develop a data structure that balances fast search with fast insertion and fast removal.
The critical optimization is to change the entry point so that it’s more convenient for binary search. Instead of entering from the beginning of the linked list, maintain a reference to the middle node. In order to access items in the left half of the data structures, flip the direction of the edges and recursively apply this pattern to each sublist.
Binary trees
What we’ve just defined is the basic structure of a binary search tree. However, before we get to working with binary search trees, we’ll start by working with a slightly simpler structure known as a binary tree.
- Binary tree
- A tree where each node has either 0, 1, or 2 children.
- Binary search tree (BST)
- A binary tree with the added binary search invariant.
public class IntTree {
private IntTreeNode overallRoot;
private static class IntTreeNode {
public int data;
public IntTreeNode left;
public IntTreeNode right;
}
}
Like linked nodes, binary tree nodes as represented by IntTreeNode
follow a recursive definition. A binary tree falls into one of the two categories.
- Empty binary tree
- The root
IntTreeNode
is null. - Non-empty binary tree
- The root
IntTreeNode
contains somedata
, aleft
subtree, and aright
subtree. Each subtree itself a binary tree.
Traversals
While there was an iterative and recursive traversal for linked lists, binary trees are much better suited for recursive traversal since each node can have two children. Similar to recursive linked list traversal, we’ll want to introduce a public/private pair to maintain a reference to the root
of the current subtree.
Unlike linked list traversal, there are three ways of traversing a binary tree depending on when the current node and each left
and right
children are processed. Consider the following IntTree
.
- Pre-order traversal
- Process the current
root
node then process theleft
andright
children.public void print() { print(overallRoot); } private void print(IntTreeNode root) { if (root != null) { System.out.print(root.data + " "); print(root.left); print(root.right); } }
17 41 29 9 81 40
- In-order traversal
- Process the
left
child, then process the currentroot
node, and then process theright
child.public void print() { print(overallRoot); } private void print(IntTreeNode root) { if (root != null) { print(root.left); System.out.print(root.data + " "); print(root.right); } }
29 41 17 81 9 40
- Post-order traversal
- Process the
left
andright
children and then process the currentroot
node.public void print() { print(overallRoot); } private void print(IntTreeNode root) { if (root != null) { print(root.left); print(root.right); System.out.print(root.data + " "); } }
29 41 81 40 9 17