给定n个关键字组成的有序序列S={s1,s2,…,sn},关键字称为实结点。对每个关键字查找的概率是Pi
查找不成功的结点称为虚结点,对应{e0,e1,…,en},每个虚结点的查找概率为qi。e0表示小于s1的值,
en大于sn的值,所有结点查找概率之和为1。求最小平均比较次数的二叉搜索树(最优二叉搜索树)。
e0<s1<e1<s2<e2…sn<en
最优二叉搜索树:是搜索成本最低的二叉搜索树,即平均比较次数最少。
每个结点的搜索成本:(结点深度+1) * 搜索概率;
解题步骤:
1.分解最优解的结构特征;
假设已知到Sk是二叉搜索树T(1,n)的根;那么问题就变成了两个问题;
{s1,s2,…,sk-1}和{e0,e1,…,ek-1}构成的左子树T(1,k-1),{sk+1,sk+2,…,sn},{ek+1,ek+2,…,en}
构成的右子树T(k+1,n)
2.建立最优值的递归式
用c[i][j]表示{si,si+1,…,sj}和{ei-1,ei,…,ej}构成的最优二叉树的最优值;
两个子问题的搜索成本的最优值分别为c[i][k-1],c[k+1][j];
实结点的搜索成本 = (深度+1)* 搜索概率p;
虚结点的搜索成本 = 深度 * 搜索概率 q
子问题1包含的结点:{si,si+1,…,sk-1}和{ei-1,ei,…,ek-1}
树根结点 : sk
子问题2包含的结点:{sk+1,sk+2,…,sj}和{ek,ek+1,…,ej}
所有结点排列:{ei-1,si,ei,…,sk,ek,…,sj,ej},
它们的概率之和为:W[i][j] = qi-1+pi+qi+…+pk+qk+…+pj+qj;
最优二叉搜索树的最优值递归式:
c[i][j] = 0 , j = i-1;
c[i][j] = min(c[i][k-1]+c[k+1][j]) + W[i][j] ,j=>i,(i <= k <= j)
w[i][j] = qi-1; j = i-1;
w[i][j] = w[i][j-1] + pj + qj , j>=i
const int M = 100;
double C[M][M], W[M][M], p[M], q[M];
int S[M][M];
int n, i, j, k;
void Optimal_BST()
{
for (i=1;i<=n;i++)
{
C[i][i - 1] = 0.0;
W[i][i - 1] = q[i - 1];
}
for (int t=1;t<=n;t++)
{
for (i = 1; i <= n - t + 1; i++)
{
j = i + t - 1;
W[i][j] = W[i][j - 1] + p[j] + q[j];
C[i][j] = C[i][i - 1] + C[i + 1][j];
S[i][j] = i;
// 选取i+1到j之间的某个下标的关键字作为i到j的根,如果组成的期望值最小
// 则k为i到j的根结点
for (k = i + 1; k < j; k++)
{
double tmp = C[i][k - 1] + C[k + 1][j];
if (tmp<C[i][j] && fabs(tmp - C[i][j])>1E-6)
{
C[i][j] = tmp;
S[i][j] = k; // 记录i到j结点的树根
}
}
C[i][j] += W[i][j];
}
}
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
if (flag == 0)
{
cout << "S" << S[i][j] << "是根" << endl;
flag = 1;
}
int k = S[i][j];
// 如果左子树是叶子
if (k - 1 < i)
{
cout << "e" << k - 1 << "is the left child of " << "S" << k << endl;
}
else
{
cout << "S" << S[i][k - 1] << " is the left child of" << "S" << k << endl;
Construct_Optimal_BST(i, k - 1, 1);
}
// 如果右子树是叶子
if (k>=j)
{
cout << "e" << j << " is the right child of " << "S" << k << endl;
}
else
{
cout << "S" << S[k + 1][j] << " is the right child of " << "S" << k << endl;
Construct_Optimal_BST(k + 1, j, 1);
}
}
int main()
{
cout << "请输入关键字的个数:" << endl;
cin >> n;
cout << "请依次输入关键字的频率:" << endl;
for (i = 1; i <= n; i++)
{
cin >> p[i];
}
cout << "请依次输入每个虚结点的搜索频率:" << endl;
for (i = 0; i <= n; i++)
{
cin >> q[i];
}
Optimal_BST();
cout << "最小的搜索成本是" << C[1][n] << endl;
cout << "最优二叉搜索树为:" << endl;
Construct_Optimal_BST(1, n, 0);
system("pause");
return 0;
}