Optimal Binary Search Tree
Given a fixed set of identifiers, the goal is to create a binary search tree. Different binary trees for the same identifier set have different performance characteristics.
- Fig (a) requires four comparisons in the worst case, and the average is .
- Fig (b) requires three comparisons in the worst case, and the average is comparisons.
- The average number of comparisons is calculated assuming that each identifier is searched for with equal probability and that no unsuccessful searches are made.
- But in a practical situation, different identifiers are searched for with different frequencies or probabilities. Also, unsuccessful searches are present.
Probabilities
Assume the given set of identifiers is {a1, a2, …, an} with a1 < a2 < … < an.
- Let be the probability with which we search for ai.
- Let be the probability that the identifier x being searched for satisfies , 0 ≤ i ≤ n.
Assume and .
is the probability of an unsuccessful search, and
External nodes
Add a fictitious (external) node in place of every empty subtree.
- If a binary search tree represents n identifiers, then there will be exactly n internal nodes and n + 1 external nodes.
- An internal node represents a point where a successful search terminates.
- An external node represents a point where an unsuccessful search terminates.
The identifiers not in the BST can be partitioned into n + 1 equivalence classes Ei, 0 ≤ i ≤ n:
- E0 contains all identifiers x such that x < a1.
- Ei contains all identifiers x such that ai < x < ai+1, 1 ≤ i < n.
- En contains all identifiers x such that x > an.
Cost of a binary search tree
The goal is to define an OBST for the identifier set {a1, a2, …, an}: a BST for which the cost is minimum.
Example
Define an OBST for (a1, a2, a3) = (do, if, while) with equal probabilities.
The number of possible BSTs for n nodes is
Solution: Given equal probabilities, i.e. for every i. The possible trees are:
Tree b is optimal.
Changed probabilities
If the probabilities are changed to p(1) = 0.5, p(2) = 0.1, p(3) = 0.05 and q(0) = 0.15, q(1) = 0.1, q(2) = 0.05, q(3) = 0.05, the cost of tree a is:
| Tree | Cost with equal probabilities | Cost with the changed probabilities |
|---|---|---|
| a | 15/7 | 2.65 |
| b | 13/7 | 1.90 |
| c | 15/7 | 1.50 |
| d | 15/7 | 2.15 |
| e | 15/7 | 1.60 |
With the changed probabilities, tree c has the least cost (1.5), so it is the optimal tree.
The handwritten page gives 2.05 for tree d. Computing it with the same formula gives 2.15, so 2.15 is shown here.
Applying dynamic programming to OBST
Make a decision as to which of the ai's should be assigned to the root node of the tree. If we choose ak, then the internal nodes for a1, a2, …, ak−1 and the external nodes for E0, E1, …, Ek−1 lie in the left subtree l of ak. The remaining nodes will be in the right subtree r of ak.
and
If w(i, j) is the sum
then the expected cost of the search tree is
If the tree is optimal, then its cost should be minimum, i.e. Cost(l) and Cost(r) should be minimum.
Let be the cost of an OBST tij containing ai+1, …, aj and Ei, …, Ej. Then
In addition, k must be chosen such that the following is minimum:
⇒ The recurrence is:
is the value of k that minimizes c(i, j) in (1). Compute c(i, j) such that j − i = 2, then for all c(i, j) with j − i = 3, and so on. (The cases j − i = 0 and j − i = 1 are set directly, as in the algorithm below.)
Example: n = 4, (a1, a2, a3, a4) = (do, if, int, while)
Use dynamic programming to generate an OBST for n = 4, (a1, a2, a3, a4) = (do, if, int, while), with P(1:4) = (3, 3, 1, 1) and q(0:4) = (2, 3, 1, 1, 1).
The probabilities are multiplied by 16 for convenience, so the actual values are P = (3/16, 3/16, 1/16, 1/16) and q = (2/16, 3/16, 1/16, 1/16, 1/16). If probabilities are given in this form, multiply them by their LCM.
There are two parts: (1) table construction and (2) OBST construction. Initially, for the first row:
For all other entries:
The completed table
| i = 0 | i = 1 | i = 2 | i = 3 | i = 4 | |
|---|---|---|---|---|---|
| j − i = 0 | w00 = 2 c00 = 0 r00 = 0 | w11 = 3 c11 = 0 r11 = 0 | w22 = 1 c22 = 0 r22 = 0 | w33 = 1 c33 = 0 r33 = 0 | w44 = 1 c44 = 0 r44 = 0 |
| j − i = 1 | w01 = 8 c01 = 8 r01 = 1 | w12 = 7 c12 = 7 r12 = 2 | w23 = 3 c23 = 3 r23 = 3 | w34 = 3 c34 = 3 r34 = 4 | |
| j − i = 2 | w02 = 12 c02 = 19 r02 = 1 | w13 = 9 c13 = 12 r13 = 2 | w24 = 5 c24 = 8 r24 = 3 | ||
| j − i = 3 | w03 = 14 c03 = 25 r03 = 2 | w14 = 11 c14 = 19 r14 = 2 | |||
| j − i = 4 | w04 = 16 c04 = 32 r04 = 2 |
Step-by-step calculations
w, c, r for (0, 1)
w, c, r for (1, 2)
w, c, r for (2, 3)
w, c, r for (3, 4)
w, c, r for (0, 2)
w, c, r for (1, 3)
w, c, r for (2, 4)
w, c, r for (0, 3)
w, c, r for (1, 4)
w, c, r for (0, 4)
Building the tree
is the minimum cost of a BST for (a1, a2, a3, a4).
- As r04 = 2, the root is a2, i.e. the left subtree is t01 and the right subtree is t24.
- As r01 = 1, a1 is the root of t01, with subtrees t00 and t11.
- As r24 = 3, a3 is the root of t24, with subtrees t22 and t34.
- As r34 = 4, a4 is the root of t34.
The total time to evaluate all the c(i, j)'s and r(i, j)'s is .
Algorithm OBST(P, q, n)
Algorithm OBST(P, q, n)
/* Given n distinct identifiers a1 < a2 < ... < an and probabilities
P[i], 1 ≤ i ≤ n, and q[i], 0 ≤ i ≤ n, this algorithm computes c[i,j]
of OBSTs t_ij for identifiers a_{i+1}, ..., a_j. It also computes
r[i,j], the root of t_ij, and w[i,j], the weight of t_ij. */
{
for i := 0 to n-1 do
{
// Initialize
w[i,i] := q[i]; r[i,i] := 0; c[i,i] := 0.0;
// OBSTs with one node
w[i,i+1] := q[i] + q[i+1] + P[i+1];
r[i,i+1] := i + 1;
c[i,i+1] := q[i] + q[i+1] + P[i+1];
}
w[n,n] := q[n]; r[n,n] := 0; c[n,n] := 0.0;
for m := 2 to n do // Find optimal trees with m nodes
for i := 0 to n-m do
{
j := i + m;
w[i,j] := w[i,j-1] + P[j] + q[j];
k := Find(c, r, i, j);
c[i,j] := w[i,j] + c[i,k-1] + c[k,j];
r[i,j] := k;
}
write(c[0,n], w[0,n], r[0,n]);
}
Algorithm Find(c, r, i, j)
Algorithm Find(c, r, i, j)
{
min := ∞;
for m := r[i,j-1] to r[i+1,j] do
if (c[i,m-1] + c[m,j]) < min then
{
min := c[i,m-1] + c[m,j]; l := m;
}
return l;
}