【pta】7-3 最优二叉搜索树

设 S={x1​,x2​,...,xn​} 是有序集,且 x1​<x2​<...<xn​,表示有序集S的二叉搜索树利用二叉树的结点来存储有序集中的元素。在该二叉搜索树中搜索一个元素x,结果有两种情形:

  1. 在二叉搜索树的内结点中找到 x=xi​(即找到了实结点),设这种情况的概率为 pi​

  2. 在二叉搜索树的叶节点中确定 x∈(xi​,xi+1​)(即找到了虚结点),设这种情况的概率为 qi​

设根节点的深度为0,存储元素 xi​的结点深度为 ci​,叶节点(xj​,xj+1​)的结点深度为 dj​,在该二叉搜索树中进行一次搜索所需的平均比较次数为 m,那么有公式:

m=∑i=1n​pi​(1+ci​)+∑j=0n​qj​dj​

m又称为二叉搜索树 T 的平均路长,本题的要求是对于有序集 S 及其存取概率分布(q0​,p1​,q1​,..,pn​,qn​),在所有表示有序集 S 的二叉搜索树中找出一颗具有最小平均路长的二叉搜索树。

image-20210225215359687.png

输入格式:

第一行给出有序集中的元素数量 n (2<n<10)

第二行给出内结点(实结点)的存取概率分布 p1​,..,pn​

第三行给出叶节点(虚结点)的存取概率分布 q0​,q1​,..,qn​

输出格式:

第一行输出最小 m ,保留两位小数。

第二行先序遍历输出二叉树,对于实结点输出其编号,对于虚结点输出.,每一个符号后有一个空格,若有多个满足要求的二叉树,则输出根节点编号最小的。

输入样例:

3
0.50 0.1 0.05
0.15 0.1 0.05 0.05

输出样例:

1.50
1 . 2 . 3 . . 

参考代码:

#include<bits/stdc++.h> // 引入C++标准库
using namespace std; // 使用标准命名空间
const int M = 100; // 定义常量M为100
double C[M][M], W[M][M], p[M], q[M]; // 定义二维数组C、W、p和q,用于存储最优二叉搜索树的代价和权重
int S[M][M]; // 定义二维数组S,用于存储最优二叉搜索树的分割点
int n, i, j, k; // 定义整型变量n、i、j、k

// 函数Optimal_BST:计算最优二叉搜索树的代价和权重
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
   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;
    }
   }
   C[i][j] += W[i][j]; // 加上当前分割点的权重
  }
 }
}

// 函数Construct_Optimal_BST:构造最优二叉搜索树
void Construct_Optimal_BST(int i,int j,bool flag)
{
 if (flag == 0) // 如果flag为0,表示这是第一次调用该函数,需要输出分割点
 {
        printf("%d ",S[i][j]); // 输出分割点
  flag = 1; // 将flag设为1,表示已经输出过分割点
 }
 int k = S[i][j]; // 获取当前分割点的分割点
 if (k - 1 < i) // 如果分割点在左子树中,需要输出"."
 {
        printf(". ");
 }
 else // 如果分割点不在左子树中,需要输出分割点的左子树的分割点
 {
        printf("%d ",S[i][k-1]);
        Construct_Optimal_BST(i, k - 1, 1); // 递归调用该函数,构造左子树的最优二叉搜索树
 }
 if (k>=j) // 如果分割点在右子树中,需要输出"."
 {
        printf(". ");
 }
 else // 如果分割点不在右子树中,需要输出分割点的右子树的分割点
 {
        printf("%d ",S[k+1][j]);
        Construct_Optimal_BST(k + 1, j, 1); // 递归调用该函数,构造右子树的最优二叉搜索树
 }
}

int main()
{
 cin >> n; // 输入节点数n
 for (i = 1; i <= n; i++) // 输入每个节点的权重p[i]
 {
  cin >> p[i];
 }
 for (i = 0; i <= n; i++) // 输入每个节点的代价q[i]
 {
  cin >> q[i];
 }
 Optimal_BST(); // 计算最优二叉搜索树的代价和权重
    printf("%.2lf\n",C[1][n]); // 输出最优二叉搜索树的代价
 Construct_Optimal_BST(1, n, 0); // 构造最优二叉搜索树并输出
 system("pause"); // 暂停程序,等待用户按任意键继续
 return 0; // 返回0,表示程序正常结束
}

 

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值