假设关键字的总数为n,用c[i,j]表示第i个关键字到第j个关键字的最优二叉查找树的代价,我们的目标是求c[0,n-1]。要求c[i,j],首先要从第i个关键字到第j个关键字中选一个出来作为根结点,选出根结点后,最优二叉搜索树的代价为左子树的代价加上右子树的代价,由于每选出一个根结点,每个关键字的搜索长度都增加1,因此得出递推式,即当
1 package org.xiu68.ch06.ex6; 2 3 public class Ex6_20 { 4 //动态规划,最优二叉搜索树 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 double[] w=new double[]{0.05,0.4,0.08,0.04,0.1,0.1,0.23}; 8 String[] words=new String[]{"begin","do","else","end","if","then","while"}; 9 int[][] roots=new int[w.length][w.length]; //i到j之间的二叉搜索树的根结点 10 optimalBST(w,roots); 11 12 for(int i=0;i<roots.length;i++){ 13 for(int j=0;j<roots[i].length;j++){ 14 System.out.print(roots[i][j]+" "); 15 } 16 System.out.println(); 17 } 18 19 printRoot(words, roots,0,w.length-1); 20 /* 21 最少平均比较次数为:2.18 22 0 1 1 1 1 1 1 23 0 1 1 1 1 1 4 24 0 0 2 2 4 4 6 25 0 0 0 3 4 4 6 26 0 0 0 0 4 4 6 27 0 0 0 0 0 5 6 28 0 0 0 0 0 0 6 29 begin和while的根结点为 do 30 begin和begin的根结点为 begin 31 else和while的根结点为 while 32 else和then的根结点为 if 33 else和end的根结点为 else 34 end和end的根结点为 end 35 then和then的根结点为 then 36 */ 37 } 38 39 //roots中存放i到j之间的二叉搜索树的根结点 40 public static void optimalBST(double[] w,int[][] roots){ 41 double[][] c=new double[w.length][w.length]; //i到j之间的二叉搜索树的最小代价 42 43 44 for(int i=0;i<w.length;i++){ 45 c[i][i]=w[i]; //树只有自身,则代价为自身的频率 46 roots[i][i]=i; //(i到j的树的根结点)自身作为根结点 47 } 48 49 for(int s=2;s<=w.length;s++){ //s个单词作为子问题(子问题的规模) 50 for(int i=0;i<=w.length-s;i++){ //s个单词的第一个单词 51 int j=i+s-1; //s个单词的最后一个单词 52 53 double min=Double.MAX_VALUE; 54 for(int k=i;k<=j;k++){ //寻找使平均查找次数最少的根结点 55 double cLeft=0; //以k作为根结点的左子树的代价 56 double cRight=0; //以k作为根结点的右子树的代价 57 if(k>i) 58 cLeft=c[i][k-1]; 59 if(k<j) 60 cRight=c[k+1][j]; 61 if(cLeft+cRight<min){ 62 min=cLeft+cRight; 63 roots[i][j]=k; 64 } 65 }//3 66 double sum=0; 67 for(int t=i;t<=j;t++){ 68 sum+=w[t]; 69 } 70 c[i][j]=min+sum; 71 }//2 72 }//1 73 System.out.println("最少平均比较次数为:"+c[0][w.length-1]); 74 } 75 76 //打印最优根 77 public static void printRoot(String[] words,int[][] roots,int i,int j){ 78 if(i<=j){ 79 int k=roots[i][j]; 80 System.out.println(words[i]+"和"+words[j]+"的根结点为 "+words[k]); 81 printRoot(words,roots,i,k-1); 82 printRoot(words,roots,k+1,j); 83 } 84 } 85 }