最优二叉查找树
最优二叉树也就是哈夫曼树,最优二叉树和最优二叉查找树是不一样的。我们说一下他们的定义
最优二叉树:
给你n个节点,每一个节点有一个权值wi。我们设一棵树的权值是所有节点的权值乘于每一个节点的深度,但是我们可以构造出来许多二叉树,我们称构造出来的那个权值最小的二叉树就是我们找的最优二叉树
求解最优二叉树:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
最优二叉查找树:
给定n个节点的值key,假设x是二叉搜索树中的一个结点。如果L是x的左子树的一个结点,那么L.key ≤ x.key。如果R是x的右子树的一个结点,那么R.key ≥ x.key。使用<key1,key2,key3....keyn>表示,且我们设定key1<key2<key3<keyn。
对于n个节点都有一个访问概率pi,使用<p1,p2,p3....pn>表示。还有未找到访问点概率qi,我们使用<q0,q1,q2,q3....qn>表示。
例如访问到[-∞,key1)的概率是q0,访问到(key1,key2)的概率是q1,,,,访问到(keyn,∞)的概率是qn。
我们设定[-∞,key1)区间为d0,(key1,key2)区间为d1,,,,(keyn,∞)区间是dn。
所以是不会出现对于i,j(1<=i,j<=n)满足keyi==keyj的情况出现
我们需要把2*n+1个节点放在一个二叉树上,其中n个节点是keyi,还有n+1个节点di。
最后形成的二叉树中叶节点肯定是di。且∑ni=1pi+∑ni=0qi=1
假定一次搜索的代价等于访问的结点数,也就是此次搜索找到的结点在二叉搜索树中的深度再加1。给定一棵二叉搜索树T,我们可以确定进行一次搜索的期望代价如下:
其中depthT表示一个结点在二叉搜索树T中的深度。
E = ∑ni=1 (depthT(keyi)+1)*pi + ∑ni=0(depthT(di)+1)*qi
= 1 + ∑ni=1 (depthT(keyi))*pi + ∑ni=0(depthT(di))*qi
我们要找到那个期望E最小的满足题意二叉树,这就是最优二叉查找树
最优二叉树是符合最优子结构的,假设由关键字子序列<keyi,keyi+1,,,,keyj>和伪关键字子序列<di-1,di,di+1,,,,,dj>构成的一棵最优二叉搜索树以kr ( i ≤ r ≤ j )为根结点。那么它的左子树由子序列<keyi,,,,keyr-1>和<di-1,,,,dr-1>构成,这颗左子树显然也是一棵最优二叉搜索树。同样,它的右子树由子序列<keyr+1,,,,keyj>和<dr,,,,dj>构成,这颗右子树显然也是一棵最优二叉搜索树。
注意一下空子树,也就是由关键字<keyi,keyj>,且当选keyi为根节点的时候,它的左子树<keyi,keyi-1>就只包含di-1,同理选keyj为根节点时,右子树只包含dj。
用e[i,j]表示包含关键字子序列<keyi,keyi+1,,,,keyj>的最优二叉搜索树的期望搜索代价。我们最终希望计算出e[1,n]。
当j=i-1时,说明此时只有di-1,故e[i,i-1] = qi-1
当j≥i时,需要从ki,……,kj中选择一个跟kr,然后用关键字ki,……,kr-1来构造一棵最优二叉查找树作为左子树,用关键字kr+1,……,kj来构造一棵最优二叉查找树作为右子树。定义一棵有关键字ki,……,kj的子树,定义概率的总和为:
因此如果kr是一棵包含关键字ki,……,kj的最优子树的根,则有:
整理得:
最终递推式:
e[i,j]给出了最优二叉搜索树子问题的期望搜索代价。我们还需要记录最优二叉搜索树子问题的根结点,用root[i,j]来记录。
给出伪代码:
代码:
#include<iostream> using namespace std; const int MAX=9999999; //const int N=5; //这是第一个例子 //float p[N+1]={0,0.15,0.10,0.05,0.1,0.20}; //float q[N+1]={0.05,0.10,0.05,0.05,0.05,0.10}; const int N=7; //这是第二个例子 float p[N+1]={0.04 ,0.06, 0.08, 0.02, 0.10, 0.12, 0.14}; float q[N+1]={0.06 ,0.06, 0.06, 0.06, 0.05, 0.05, 0.05, 0.05}; float e[N+2][N+1]; int root[N+1][N+1]; float w[N+2][N+1]; void optimal_bst_search_tree(float p[],float q[],int n) { int i; for(i=1;i<=n+1;i++) { e[i][i-1]=q[i-1]; w[i][i-1]=q[i-1]; } int l,j,r; for(l=1;l<=n;l++) //枚举区间长度,也就是e[i][j]的j-i+1的长度 { for(i=1;i<=n-l+1;i++) { j=i+l-1; e[i][j]=MAX; w[i][j]=w[i][j-1]+p[j]+q[j]; for(r=i;r<=j;r++) { double t=e[i][r-1]+e[r+1][j]+w[i][j]; if(t<e[i][j]) { e[i][j]=t; root[i][j]=r; } } } } } void print_root() { int i,j; cout<<"各子树的根:"<<endl; for(i=1;i<=N;i++) { for(j=1;j<=N;j++) cout<<root[i][j]<<" "; cout<<endl; } } void construct_optimal_bst(int i,int j) { if(i<=j) { int r=root[i][j]; cout<<r<<" "; construct_optimal_bst(i,r-1); construct_optimal_bst(r+1,j); } } void print_bst(int i,int j) { if(i==1&&j==N) cout<<"root is "<<root[i][j]<<endl; if(i<j) { int r=root[i][j]; if(i!=r) cout<<"left child root "<<root[i][r-1]<<endl; print_bst(i,root[i][j]-1); if(j!=r) cout<<"right child root "<<root[r+1][j]<<endl; print_bst(root[i][j]+1,j); } } int main() { optimal_bst_search_tree(p,q,N); print_root(); cout<<"构造的最优二叉树:"<<endl; construct_optimal_bst(1,5); cout<<endl; print_bst(1,N); return 0; } //0.04 ,0.06, 0.08, 0.02, 0.10, 0.12, 0.14 //0.06 0.06 0.06 0.06 0.05 0.05 0.05 0.05
第二个例子的最优二叉查找树如下:算出来期望是3.12