第十章动态规划(最优二叉搜索树)

给定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;
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值