1、概念引入
基於統計先驗知識,我們可統計出一個數表(集合)中各元素的查找概率,理解為集合各元素的出現頻率。比如中文輸入法字庫中各詞條(單字、詞組等)的先驗概率,針對用戶習慣可以自動調整詞頻——所謂動態調頻、高頻先現原則,以減少用戶翻查次數。這就是最優二叉查找樹問題:查找過程中鍵值比較次數最少,或者說希望用最少的鍵值比較次數找到每個關鍵碼(鍵值)。為解決這樣的問題,顯然需要對集合的每個元素賦予一個特殊屬性——查找概率。這樣我們就需要構造一顆最優二叉查找樹。
2、問題給出
n個鍵{a1,a2,a3......an},其相應的查找概率為{p1,p2,p3......pn}。構成最優BST,表示為T
1
n ,求這棵樹的平均查找次數C[1, n](耗費最低)。換言之,如何構造這棵最優BST,使得
C[1, n] 最小。
3、分段方法
動態規划法策略是將問題分成多個階段,逐段推進計算,后繼實例解由其直接前趨實例解計算得到。對於最優BST問題,利用減一技術和最優性原則,如果前n-1個節點構成最優BST,加入一個節點
a
n 后要求構成規模n的最優BST。按 n-1, n-2 , ... , 2, 1 遞歸,問題可解。自底向上計算:C[1, 2]→C[1, 3] →... →C[1, n]。為不失一般性用
C[i, j] 表示由{a1,a2,a3......an}構成的BST的耗費。其中1≤i ≤j ≤n。這棵樹表示為Tij。從中選擇一個鍵ak作根節點,它的左子樹為Tik-1,右子樹為Tk+1j。要求選擇的k 使得整棵樹的平均查找次數C[i, j]最小。左右子樹遞歸執行此過程。(根的生成過程)
4、遞推計算式

5、基本算法如下

6、具體實現代碼(其中所有數據都存放在2.txt中,其內容為:

其中5表示有5個節點,其他數據表示各個節點出現的概率;
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define max 9999 4 void OptimalBST(int,float*,float**,int**); 5 void OptimalBSTPrint(int,int,int**); 6 void main() 7 { 8 int i,num; 9 FILE *point; 10 //所有數據均從2.txt中獲取,2.txt中第一個數據表示節點個數;從第二個數據開始表示各個節點的概率 11 point=fopen("2.txt","r"); 12 if(point==NULL) 13 { 14 printf("cannot open 2.txt.\n"); 15 exit(-1); 16 } 17 fscanf(point,"%d",&num); 18 printf("%d\n",num); 19 float *p=(float*)malloc(sizeof(float)*(num+1)); 20 for(i=1;i<num+1;i++) 21 fscanf(point,"%f",&p[i]); 22 //創建主表; 23 float **c=(float**)malloc(sizeof(float*)*(num+2)); 24 for(i=0;i<num+2;i++) 25 c[i]=(float*)malloc(sizeof(float)*(num+1)); 26 //創建根表; 27 int **r=(int**)malloc(sizeof(int*)*(num+2)); 28 for(i=0;i<num+2;i++) 29 r[i]=(int*)malloc(sizeof(int)*(num+1)); 30 //動態規划實現最優二叉查找樹的期望代價求解。。 31 OptimalBST(num,p,c,r); 32 printf("該最優二叉查找樹的期望代價為:%f \n",c[1][num]); 33 //給出最優二叉查找樹的中序遍歷結果; 34 printf("構造成的最優二叉查找樹的中序遍歷結果為:"); 35 OptimalBSTPrint(1,4,r); 36 37 } 38 void OptimalBST(int num,float*p,float**c,int**r) 39 { 40 int d,i,j,k,s,kmin; 41 float temp,sum; 42 for(i=1;i<num+1;i++)//主表和根表元素的初始化 43 { 44 45 c[i][i-1]=0; 46 c[i][i]=p[i]; 47 r[i][i]=i; 48 } 49 c[num+1][num]=0; 50 for(d=1;d<=num-1;d++)//加入節點序列 51 { 52 for(i=1;i<=num-d;i++) 53 { 54 j=i+d; 55 temp=max; 56 for(k=i;k<=j;k++)//找最優根 57 { 58 if(c[i][k-1]+c[k+1][j]<temp) 59 { 60 temp=c[i][k-1]+c[k+1][j]; 61 kmin=k; 62 } 63 } 64 r[i][j]=kmin;//記錄最優根 65 sum=p[i]; 66 for(s=i+1;s<=j;s++) 67 sum+=p[s]; 68 c[i][j]=temp+sum; 69 } 70 } 71 } 72 //采用遞歸方式實現最優根的輸出,最優根都是保存在r[i][j]中的。。。 73 void OptimalBSTPrint(int first,int last,int**r) 74 { 75 76 int k; 77 if(first<=last) 78 { 79 k=r[first][last]; 80 printf("%d ",k); 81 OptimalBSTPrint(first,k-1,r); 82 OptimalBSTPrint(k+1,last,r); 83 } 84 }
7、最終運行結果:

8、參考文獻:
(1)算法導論
(2)數據結構 嚴蔚敏
(3)網上下載的一個ppt(算法設計與分析,第八章)
