問題描述
給定一個由n個互異的關鍵字組成的序列K={k1,k2,...,kn},且關鍵字有序,對於每一個關鍵字ki,一次搜尋為ki的機率是pi。某些搜尋的值可能不在K內,因此還有n+1個虛擬鍵d0,d1,...,dn代表不再K內的值。d0代表所有小於k1的值,dn代表所有大於kn的值,對於i=1,2,...,n-1,di代表所有位於ki和ki+1之間的值。對每個虛擬鍵di,一次搜尋對應於di的機率是qi。定義在T內一次搜尋的期望代價為E=∑(depth(ki)+1)*pi+∑(depth(di)+1)*qi=1+∑depth(ki)*pi+∑depth(di)*qi
一棵最優二叉尋找樹就是期望代價最小的BST。
注意:
1)一顆最優二叉樹不一定是一顆整體高度最小的樹;也不一定總把具有最大機率的關鍵字作為根節點。
2)二叉尋找樹的子樹必定包含連續範圍內的關鍵字。
3)當一顆樹成為一個節點的子樹時,它的期望代價增加值為該樹中所有機率的總和。
最優子結構
設包含有序關鍵字(ki, ..., kj)的最優二叉尋找樹以kr(i≤r≤j)為根節點,則其左子樹(ki, ..., kr-1)和右子樹(kr+1, ..., kj)也同樣為最優二叉尋找樹。
狀態轉移方程
e[i,j]表示搜尋一棵包含關鍵字ki...kj的最優二叉樹的平均代價。
w[i,j]表示含關鍵字ki...kj的子樹的機率總和。
代碼實現
//最優二叉尋找樹,利用動態規劃實現#include<iostream>using namespace std;void Optimal_BST(double *p,double *q,int length,double (*e)[20],int (*root)[20]){int i,j,k,r;double t;double w[20][20]={0};for(i=1;i<=length+1;i++){e[i][i-1]=q[i-1];w[i][i-1]=q[i-1];}//i為關鍵字之間的長度for(i=1;i<=length;i++){//從下標為j開始的關鍵字到下標為k的關鍵字for(j=1;j<=length-i+1;j++){k=i+j-1;e[j][k]=0x7fffffff;w[j][k]=w[j][k-1]+p[k]+q[k];//選取j到k之間的某個下標的關鍵字作為從j到k的根,如果組成的樹的期望值當前最小,則r為從j//到k的根節點for(r=j;r<=k;r++){t=e[j][r-1]+e[r+1][k]+w[j][k];if(e[j][k]>t){e[j][k]=t;//r即為從下標j到k的根節點root[j][k]=r;}}}}}void Construct_Optimal_BST(int (*root)[20],int i,int j,bool flag){if(flag==0){cout<<"k"<<root[i][j]<<" 是根"<<endl;flag=1;}int r=root[i][j];//如果左子樹是葉子if(r-1<i){cout<<"d"<<r-1<<" is the left child of "<<"K"<<r<<endl;}//如果左子樹不是葉子else{cout<<"k"<<root[i][r-1]<<" is the left child of "<<"K"<<r<<endl;Construct_Optimal_BST(root,i,r-1,1);}//如果右子樹是葉子if(r>=j){cout<<"d"<<j<<" is the right child of "<<"K"<<r<<endl;}//如果右子樹不是葉子else{cout<<"k"<<root[r+1][j]<<" is the right child of "<<"K"<<r<<endl;Construct_Optimal_BST(root,r+1,j,1);}}int main(){double p[8]={0,0.04,0.06,0.08,0.02,0.10,0.12,0.14};double q[8]={0.06,0.06,0.06,0.06,0.05,0.05,0.05,0.05};double e[20][20]={0};int root[20][20]={0};Optimal_BST(p,q,6,e,root);cout<<e[1][5]<<endl;Construct_Optimal_BST(root,1,5,0);return 0;}
運行結果:
參考
最優二叉尋找樹-http://blog.csdn.net/liuzhanchen1987/article/details/7853219