最优二叉搜索树
问题描述
二叉搜索树
二叉搜索树满足如下性质:假设 是二叉搜索树中的一个结点。如果 是 的左子树的一个结点,那么 。如果 是 的右子树的一个结点,那么 。
也就是说,二叉搜索树中的任意一个结点,它的左子树中的所有结点都不大于它,它的右子树中的所有结点都不小于它。
最优二叉搜索树
假设有 个有序的 ,每个 搜索概率不同,除 之外的值(即搜索不成功情况)搜索概率也不同,构造平均搜索长度最小的二叉树。
上表给出了此问题的一个输入样例。 表示搜索成功的概率,例如搜索到 的概率为 。 表示搜索失败的概率,落在了某两个 之间或者某个 之外。例如,小于 的元素的搜索概率为 ,大于 而小于 的元素的搜索概率为 ,以此类推。
问题分析
如果优化二叉搜索树 具有包含关键字集合 子树 ,则 必是关于关键字集合 子问题的最优解。这说明问题的最优解包括子问题最优解,可以运用动态规划的方法避免子问题的重复计算。
表示 构造的最优二叉树的代价(平均搜索长度),则 是最后结果。 表示权值,为 与 之和。
关键字 以 为根的最优二叉树代价为:
其中, 为根的代价, 为左子树的代价, 为左子树加一层的代价, 为右子树的代价, 为右子树加一层的代价。
因而,关键字 的最优二叉树代价为:
其中, 为树上权值和。上述方程即为本题的状态转移方程。
问题求解
首先各 自成二叉搜索树,计算平均搜索长度,保留最小值。代价数组 的主对角线初始化。
接下来,计算相邻 个、 个 的平均搜索长度,保留最小值,直至计算到第 个 。代价数组 开始向右上角发展,最终右上角的结果即为解。
算法代码
double Optimal_BST(double *p, double *q, int n)
{
// 初始化
double **c = new double *[n + 1];
double **w = new double *[n + 1];
for (int i = 0; i < n + 1; i++)
{
c[i] = new double[n + 1];
w[i] = new double[n + 1];
c[i][i] = 0;
w[i][i] = q[i];
}
// 计算
for (int x = 1; x < n + 1; x++) // x 表示计算相邻的 x+1 个子树
{
for (int i = 0; i < n - x; ++i)
{
int j = i + x;
c[i][j] = 65535; // 初始化为无穷大
w[i][j] = w[i][j - 1] + p[j] + q[j];
for (int k = i; k < j + 1; ++k) // 以 k 为根
{
double t = w[i][j];
if (k - 1 >= i) // 注意边界,下同
{
t += c[i][k - 1];
}
if (k + 1 <= j)
{
t += c[k + 1][j];
}
if (t < c[i][j]) // 保留最小值
{
c[i][j] = t;
}
}
}
}
double res = c[0][n];
// 释放堆空间
for (int i = 0; i < n + 1; i++)
{
delete c[i];
delete w[i];
}
delete c;
delete w;
return res;
}
算法分析
- 时间复杂度:
- 空间复杂度: