1.最优二叉树定义
扩展二叉树:在对二叉搜索树进行搜索的时候,会遇到检索不成功的情况。当二叉搜索树出现空的子叶时,我们把它补全(以区间的形式),因此扩展二叉树是满二叉树。
最优二叉树:
即bi表示找到结点的概率,
- a0表示找到区间(负无穷,x1)的概率
- ai表示找到区间(xi,xi+1)的概率
- an表示找到区间(xn,正无穷)的概率
对于二叉树中不能搜索到的结点(区间)我们称它伪结点,若树的根节点深度是0,那么对于每个深度为ci的结点,查找次数就是(ci+1),此结点对应的伪结点的深度是dj=ci+1,然而此伪结点查找次数是和其对应父亲节点一样的,即ci+1,即dj
2.最优子结构的证明
即证明对于最优二叉树其左右子树也是最优的。
证明1(文字证明):
若k为s树根,则结点1,2……k-1,伪结点0,1,……k-1位于左子树,结点k+1,k+2……n和伪结点k,k+1……n位于右子树上。若在左子树或右子树中能找到另一个更优的二叉树,那么对于整个树来说,情况就不是最优的了(反证法,切割粘贴法)。
证明2(数学证明):
mij=wij*pij表示对Tij进行一次访问的平均代价
wij表示Tij所有节点概率之和
sij表示子树Tij的根节点
其中wm,m*1是因为以m为根节点,m在子树Ti,j中的深度是1
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100],b[100],w[100][100],m[100][100],s[100][100];
void print(int i,int j){
if(i<j){
int root=s[i][j];
printf("S%d是根\n",root);
if(s[i][root-1]>0) {
printf("S%d的左孩子是S%d\n",root,s[i][root-1]);
}
if(s[root+1][j]>0) {
printf("S%d的右孩子是S%d\n",root,s[root+1][j]);
}
print(i,root-1);
print(root+1,j);
}
}
int main(){
int n;
printf("请输入结点数目:\n");
scanf("%d",&n);
printf("请输入各结点查找成功的概率和失败的概率:\n");
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<=n;i++){w[i+1][i]=a[i];m[i+1][i]=0;}
//w[i+1][i]的值为其对应伪节点的值,对于m数组,mi,j若i>j则说明没有节点只有伪节点,访问代价为0
for(int r=0;r<n;r++) {//长度
for (int i=1;i<=n-r;i++){
int j=i+r;
w[i][j]=w[i][j-1]+a[j]+b[j];//{ai-1,bi,…aj,bj}={ai-1,bi,…bi-1,aj-1}+aj+bj;
m[i][j]=m[i+1][j];//i为根节点,i又是树Ti,j的左边界,所以mi,j赋右子树代价mi+1,j
s[i][j]=i;
//以上三行是以i为根节点的情况
for (int k=i+1;k<=j;k++) {
int t=m[i][k-1]+m[k+1][j];//左子树代价加右子树代价
if(t<m[i][j]){m[i][j]=t;s[i][j]=k;}
}
m[i][j]+=w[i][j];
}
}
print(1,n);
}
/*
5
15 10 5 10 20
5 10 5 5 5 10
*/