最優二叉搜索樹
(1)二叉搜索樹 (二分檢索樹)二叉搜索樹T是一棵二元樹,它或者為空,或者其每個結點含有一個可以比較大小的數據元素,且有:
a·T的左子樹的所有元素比根結點中的元素小;
b·T的右子樹的所有元素比根結點中的元素大;
c·T的左子樹和右子樹也是二叉搜索樹。
(2)最優二叉搜索樹
給定一個n個關鍵字的已排序的序列K=<k 1 ,k 2 ,…,k n >( 不失一般性,設k 1 <k 2 <…<k n ),對每個關鍵字k i ,都有一個概率p i 表示其搜索頻率。根據k i和p i 構建一個二叉搜索樹T,每個k i 對應樹中的一個結點。若搜索對象x等於某個k i ,則一定可以在T中找到結點k i ;若x<k 1 或 x>k n 或 k i <x<k i+1 (1≤i<n), 則在T中將搜索失敗。為此引入外部結點d 0 ,d 1 ,…,d n ,用來表示不在K中的值,稱為偽結點。這里每個d d i i 代表一個區間, d 0 表示所有小於k 1 的值, d n 表示所有大於k n 的值,對於i=1,2,…,n-1,d i 表示所有在k i 和k i+1 之間的值。 同時每個d i 也有一個概率q q i i 表示搜索對象x恰好落入區間d i 的頻率。
(3)例:設有n=5個關鍵字的集合,每個k i 的概率p i 和d i 的概率q i 如表所示:其中
基於該集合,兩棵可能的二叉搜索樹如下所示。
(4)二叉搜索樹的期望搜索代價代價等於從根結點開始訪問結點的數量。從根結點開始訪問結點的數量等於結點在T中的深度+1; 二叉搜索樹T的期望代價記depth T (i)為結點i在T中的深度,則T搜索代價的期望為:
上面圖中(a)的期望搜索代價為2.80。(b)的期望搜索代價為2.75。
最優二叉搜索樹的定義對給定的概率集合,期望搜索代價最小的二叉搜索樹稱為最優二叉搜索樹
(1)最優二叉搜索樹的最優子結構:如果一棵 最優二叉搜索樹T T有一棵包含關鍵字k i ,…,k j 的子樹T’,則T’必然是包含關鍵字k i ,…,k j 和偽關鍵字d i-1 ,…,d j 的子問題的最優解。
(2)構造最優二叉搜索樹利用最優二叉搜索樹的最優子結構性來構造最優二叉搜索樹。分析:
對給定的關鍵字k i ,…,k j ,若其最優二叉搜索樹的根結點是k r(i≤r≤j),則k r 的左子樹中包含關鍵字k i ,…,k r-1 及偽關鍵字d i-1,…,d r-1 ,右子樹中將含關鍵字k i+1 ,…,k j 及偽關鍵字d r ,…,d j 。策略:檢查所有可能的根結點k r (i≤r≤j),如果事先分別求出包含關鍵字k i ,…,k r-1 和關鍵字k r+1 ,…,k j 的最優二叉搜索子樹,則可保證找到原問題的最優解。
(3)計算過程: 求解包含關鍵字k i ,…,k j 的最優二叉搜索樹,其中,i≥1,j≤n 且 j≥i-1。
定義e[i,j]:為包含關鍵字k i ,…,k j 的最優二叉搜索樹的期望搜索代價。e[1,n]為問題的最終解。
當j≥i時,我們需要從k i ,…,k j 中選擇一個根結點k r ,其左子樹包含關鍵字k i ,…,k r-1 且是最優二叉搜索子樹,其右子樹包含關鍵字k r+1 ,…,k j 且為最優二叉搜索子樹。
(4)若k r 為包含關鍵字k i ,…,k j 的最優二叉搜索樹(樹根),則其期望搜索代價與左、右子樹的期望搜索代價e[i,r-1]和e[r+1,j]有如下遞推關系:
其中,,所以得出
(5)有上面的分析,可以得到kr的遞推公式
邊界條件:j=i-1,存在e[i, i-1]和e[j+1, j]的邊界情況。此時,子樹不包含實際的關鍵字,而只包含偽關鍵字d i-1 ,其期望搜索代價為:
(6)構造最優二叉搜索樹
定義root[i,j],保存計算e[i, j]時使e[i, j]取得最小值的r。在求出e[1,n]后,利用root的記錄構造出整棵最優二叉搜索樹。
(7)計算最優二叉搜索樹的期望搜索代價
e[1..n+1,0..n]:
用於記錄所有e[i,j]的值。注:e[n+1,n]對應偽關鍵字d n 的子樹;e[1,0]對應偽關鍵字d 0 的子樹。
root[1..n]:用於記錄所有最優二叉搜索子樹的根結點,包括整棵最優二叉搜索樹的根。
w[1..n+1,0..n]:用於子樹保存增加的期望搜索代價,且有
這樣,對於Θ(n 2 )個w[i,j],每個的計算時間僅為Θ(1)。
上面算法是構造root表的算法
舉例說明
(1)給出一組數據
(2)根據求出w表的內容
(3)根據求出e表的內容
(4)最后根據上面的算法得出root表的內容
(5)下面給出C++的最優二叉搜索樹的代碼實現

1 #include <iostream> 2 3 using namespace std; 4 5 const int MaxVal = 9999; 6 7 const int n = 5; 8 //搜索到根節點和虛擬鍵的概率 9 double p[n + 1] = {-1,0.15,0.1,0.05,0.1,0.2}; 10 double q[n + 1] = {0.05,0.1,0.05,0.05,0.05,0.1}; 11 12 int root[n + 1][n + 1];//記錄根節點 13 double w[n + 2][n + 2];//子樹概率總和 14 double e[n + 2][n + 2];//子樹期望代價 15 16 void optimalBST(double *p,double *q,int n) 17 { 18 //初始化只包括虛擬鍵的子樹 19 for (int i = 1;i <= n + 1;++i) 20 { 21 w[i][i - 1] = q[i - 1]; 22 e[i][i - 1] = q[i - 1]; 23 } 24 25 //由下到上,由左到右逐步計算 26 for (int len = 1;len <= n;++len) 27 { 28 for (int i = 1;i <= n - len + 1;++i) 29 { 30 int j = i + len - 1; 31 e[i][j] = MaxVal; 32 w[i][j] = w[i][j - 1] + p[j] + q[j]; 33 //求取最小代價的子樹的根 34 for (int k = i;k <= j;++k) 35 { 36 double temp = e[i][k - 1] + e[k + 1][j] + w[i][j]; 37 if (temp < e[i][j]) 38 { 39 e[i][j] = temp; 40 root[i][j] = k; 41 } 42 } 43 } 44 } 45 } 46 47 //輸出最優二叉查找樹所有子樹的根 48 void printRoot() 49 { 50 cout << "各子樹的根:" << endl; 51 for (int i = 1;i <= n;++i) 52 { 53 for (int j = 1;j <= n;++j) 54 { 55 cout << root[i][j] << " "; 56 } 57 cout << endl; 58 } 59 cout << endl; 60 } 61 62 //打印最優二叉查找樹的結構 63 //打印出[i,j]子樹,它是根r的左子樹和右子樹 64 void printOptimalBST(int i,int j,int r) 65 { 66 int rootChild = root[i][j];//子樹根節點 67 if (rootChild == root[1][n]) 68 { 69 //輸出整棵樹的根 70 cout << "k" << rootChild << "是根" << endl; 71 printOptimalBST(i,rootChild - 1,rootChild); 72 printOptimalBST(rootChild + 1,j,rootChild); 73 return; 74 } 75 76 if (j < i - 1) 77 { 78 return; 79 } 80 else if (j == i - 1)//遇到虛擬鍵 81 { 82 if (j < r) 83 { 84 cout << "d" << j << "是" << "k" << r << "的左孩子" << endl; 85 } 86 else 87 cout << "d" << j << "是" << "k" << r << "的右孩子" << endl; 88 return; 89 } 90 else//遇到內部結點 91 { 92 if (rootChild < r) 93 { 94 cout << "k" << rootChild << "是" << "k" << r << "的左孩子" << endl; 95 } 96 else 97 cout << "k" << rootChild << "是" << "k" << r << "的右孩子" << endl; 98 } 99 100 printOptimalBST(i,rootChild - 1,rootChild); 101 printOptimalBST(rootChild + 1,j,rootChild); 102 } 103 104 int main() 105 { 106 optimalBST(p,q,n); 107 printRoot(); 108 cout << "最優二叉樹結構:" << endl; 109 printOptimalBST(1,n,-1); 110 }
輸出結果