最優二叉查詢樹
最優二叉樹也就是哈夫曼樹,最優二叉樹和最優二叉查詢樹是不一樣的。我們說一下他們的定義
最優二叉樹:
給你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 =∑n
= 1 + ∑ni=1(depthT(keyi))*pi +∑ni=0(depthT(keyi))*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>,且當選ki為根節點的時候,它的左子樹<keyi,keyi-1>就只包含di-1,同理選kj為根節點時,右子樹只包含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