前面的optimal-bst还能写出来,要去construct就不会了,也是理解不够深刻的原因吧。以下代码的construct用了两种方法,参照了两位博主,前一个代码风格非常暴力,能看懂以后写不出来系列。第二种的d的输出那我没有看出逻辑,好像是用的输出的相邻两个下标是在同一个节点上连着?这个的理由我没想出来(我也不确定该博主代码是否正确,但输出是对的)。
参照 https://blog.csdn.net/c18219227162/article/details/50429597
我在看最优二叉搜索树的时候遇到一点问题(现在也觉得没有很透彻理解这个)
e[i][j]的递归式我就看了好一会…..首先联系最开始提出的英语翻译成法语,如果搜索的内容没有,也就是书上的d0,d1,d2……,做的处理就是在二维数组e[i][j]中j=i-1,为什么搜索的期望是q[i-1]呢,当j=i-1的时候表示的是没有真实关键字,只有虚拟键,那深度就是0,再加一,乘以概率最后就是概率了。当i<=j的时候,我开始就没想通为什么后面加个w(i,j),w(i,j)是表示的某子树的概率和,e[i][j]表示的是期望。在算法里面,我们是在找root也就是kr,找到root之后,为这棵树构造一个最优的左子树和最优的右子树,也就是在左边和右边都还要再找一个root,构造成左子树和右子树,这个时候子树的搜索代价就变了,子树中的每个节点深度都增加了1(一棵树变成一个结点的子树时深度+1)。结合搜索的期望的公式,(深度+1)*p+(深度+1)*q那个公式,可以看出最后就是多了一个概率和。左子树的e加上右子树的e加上一个w就行了。
还有就是写construct的时候,不知道怎么从一个root得到他的左子树和右子树是什么。
printf(“k%d是k%d的左孩子\n”,root[i][root[i][j]-1],root[i][j]);
printf(“k%d是k%d的右孩子\n”, root[root[i][j] + 1][j], root[i][j]);
其实这一点,看递推公式那里就能看出来。
#include "stdafx.h"
#include
#define n 5
int root[n+1][n+1]; //树根
double e[n + 2][n + 2]; //e是当前的搜索期望
double w[n + 2][n + 2]; //w是每个子树上的概率和
double p[n+1] = { -1,0.15,0.1,0.05,0.10,0.20 }; //关键字的概率
double q[n+1] = { 0.05,0.10,0.05,0.05,0.05,0.10 }; //虚拟键的概率
void OPTIMAL_BST(double *p, double *q)
{
int i,l,r,j;
double t;
for (i = 1; i <= n + 1; i++) //当j=i-1就是虚拟键的情况
{
e[i][i - 1] = q[i - 1];
w[i][i - 1] = q[i - 1];
}
for (l = 1; l <= n; l++)
{
for (i = 1; i <= n - l + 1; i++)
{
j = i + l - 1;
e[i][j] = 0x7fffffff; //先赋值无穷
w[i][j] = w[i][j - 1] + p[j] + q[j]; //w递归 ,注意pq的下标相同,也就是假如当用到k2,d2时他们调用取得是相同的下标,所以p,q长度不同把p前面弄了一个-1
for (r = i; r <= j; r++) // r代表的是k或d的下标
{
t = e[i][r - 1] + e[r + 1][j] + w[i][j]; //e[i][j]的递归
if (t < e[i][j])
{
e[i][j] = t; //e[i][j]取最小值
root[i][j] = r; //当代价取到最小值的时候,i到j的根存在root[i][j]中
}
}
}
}
}
/*
void CONSTRUCT_OPTIMAL_BST(int r, int i, int j)
{
int child = root[i][j];
if (child == root[1][n])
{
printf("整棵树的根是:k%d\n", root[1][n]); //1到n是整棵树的根
CONSTRUCT_OPTIMAL_BST(child, i, child - 1);
CONSTRUCT_OPTIMAL_BST(child, child + 1, j);
return; //return;在void里面强制退出,可设置断点跟踪看清用法
}
if (j < i - 1)
return ;
else if (j == i - 1) //虚拟键
{
if (j < r)
printf("d%d是k%d的左孩子\n", j, r);
else
printf("d%d是k%d的右孩子\n", j, r);
return ;
}
else //关键字
{
if (child < r)
printf("k%d是k%d的左孩子\n", child, r);
else
printf("k%d是k%d的右孩子\n", child, r);
}
CONSTRUCT_OPTIMAL_BST(child,i,child - 1);
CONSTRUCT_OPTIMAL_BST(child,child+1,j);
}
*/
void CONSTRUCT_OPTIAML_BST(int root[][n+1], int i, int j)
{
if(i==1&&j==n)
printf("整棵树的根是:k%d\n", root[1][n]);
if (i < j)
{
printf("k%d是k%d的左孩子\n",root[i][root[i][j]-1],root[i][j]);
CONSTRUCT_OPTIAML_BST(root, i, root[i][j] - 1);
if (root[i][j] + 1 < j)
printf("k%d是k%d的右孩子\n", root[root[i][j] + 1][j], root[i][j]);
CONSTRUCT_OPTIAML_BST(root, root[i][j] + 1, j);
}
if (i == j)
{
printf("d%d是K%d的左孩子\n", i - 1, i);
printf("d%d是K%d的右孩子\n", i, i);
}
if (i > j)
printf("d%d是K%d的右孩子\n", j, j);
}
int main()
{
OPTIMAL_BST(p, q);
//CONSTRUCT_OPTIMAL_BST(-1, 1, n);
CONSTRUCT_OPTIAML_BST(root, 1, n);
}