問題:假設n個已經排序的關鍵字{1,2,…,n},他們被搜尋的機率分别為{p1,p2,…,pn},其它搜尋值則被這些關鍵字分割,它們的機率為{q0,q1,…,qn}。q0代表搜尋值小于1的可能性,qn代表搜尋值大于n的可能性,qi(i=1,2,,…,n-1)代表搜尋之大于i小于i+1的可能性。建構二叉搜尋樹,使得值搜尋的期望代價最小。
分析:令a[i][j]代表i->j的最優二叉搜尋樹的期望代價。于是我們可以得到以下遞推式:

這個算法的複雜度為O(n^3)。
Knuth已經證明對于所有的1 <= i < j <= n,總存在最優子樹的根使得root[i][j-1] <= root[i][j] <= root[i+1][j]。是以隻要在i<j時,在求min操作中,使root[i][j-1] <= k <= root[i+1][j]。此時算法複雜度為O(n^2)。
代碼:
#include <iostream>
#define MAXLENGTH 100
#define MAX 100
#define LEFT false
#define RIGHT true
using namespace std;
/**
* @brief Create the optimal binary search tree. The time complexity is O(n^3).
*
* @param p[]: the possibility of inner nodes. The nodes are assumed to have been sorted by their key
* @param q[]: the possibility of outer nodes. These nodes have the keys who are not in the binary search tree
* @param n: the number of inner nodes
* @param a[][MAXLENGTH]: a[i][j] means the minimum cost of tree i->j
* @param root[][MAXLENGTH]: root[i][j] means the root of tree i->j
*/
void OptimalBinarySearchTree(double p[], double q[], int n, double a[][MAXLENGTH], int root[][MAXLENGTH])
{
double w[MAXLENGTH][MAXLENGTH];
//initial states
for (int i = 0; i <= n; ++i)
{
a[i + 1][i] = q[i];
w[i + 1][i] = q[i];
}
for (int len = 1; len <= n; ++len)
{
for (int i = 1; i <= n - len + 1; ++i)
{
int j = i + len - 1;
a[i][j] = MAX;
w[i][j] = w[i][j - 1] + p[j] + q[j];
//if use {for (int k = root[i][j-1]; k <= root[i+1][j]; ++k)}, the time complexity will be O(n^2)
//this method only applies to such a situation when 1 <= i < j <= n, so when len == 1, we should not use this method
for (int k = i; k <= j; ++k)
{
double temp = a[i][k - 1] + a[k + 1][j] + w[i][j];
//get the minimum cost
if (temp < a[i][j])
{
a[i][j] = temp;
root[i][j] = k;
}
}
}
}
}
/**
* @brief Print the tree
*
* @param root[][MAXLENGTH]: the root matrix
* @param start: the start position of the tree
* @param end: the end position of the tree
* @param k: the parent, -1 means root has no parent
* @param LeftOrRight: the left or right subtree of the parent
*/
void PrintTree(int root[][MAXLENGTH], int start, int end, int k, bool LeftOrRight)
{
if (k == -1)
{
cout << "the root is p" << root[start][end] << endl;
PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);
PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);
}
else if (LeftOrRight == LEFT)
{
if (start <= end)
{
cout << "the left child of p" << k << " is p" << root[start][end] << endl;
PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);
PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);
}
else
{
cout << "the left child of p" << k << " is q" << start - 1 << endl;
}
}
else
{
if (start <= end)
{
cout << "the right child of p" << k << " is p" << root[start][end] << endl;
PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);
PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);
}
else
{
cout << "the right child of p" << k << " is q" << start - 1 << endl;
}
}
}
int main()
{
double p[MAXLENGTH];
double q[MAXLENGTH];
double a[MAXLENGTH][MAXLENGTH];
int root[MAXLENGTH][MAXLENGTH];
int n;
while (cin >> n, n != 0)
{
for (int i = 1; i <= n; ++i)
{
cin >> p[i];
}
for (int i = 0; i <= n; ++i)
{
cin >> q[i];
}
OptimalBinarySearchTree(p, q, n, a, root);
cout << a[1][n] << endl;
PrintTree(root, 1, n, -1, true);
}
}