OBST问题:用二叉树来存数据,不同数据访问频率不一样,怎么样构建这颗二叉树,才可以使得访问代价最小。
直观的想法是把访问频率最高的放到树的根节点,树越矮越好,但是,很容易举出例子证明这种想法是错的,正确的做法是用动态规划。
先讲讲用到的变量:
p[ n ]是n个节点的频率数组,
e[ i ][ j ] 表示第 i 个节点到第 j 个节点构成的子树的最小搜索代价值,
w[ i ][ j ]表示第 i 个节点到第 j 个节点的频率和。
一个事实:如果r是e[ i ][ j ]的根节点(i<=r<=j)那么:
e[ i ][ j ]=e[ i ][ r-1 ] + e[ r+1 ][ j ] +w[ i ][ j ] ( i<=j )
同时定义边界:e[ i ][ j ]=0 ( j=i-1 )
动态转移方程:
e[ i ][ j ]=min{ e[ i ][ r-1 ] + e[ r+1 ][ j ] +w[ i ][ j ] }
用到了3层循环,第一层循环变量是子树的节点个数,第二层循环的变量是子树的起点位置,第三层循环的变量是子树的根节点位置,如此,我们可以得到任意区间OBST的搜索期望是多少。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #define INF 9999; 5 6 void Optimal_BST(double p[],int n){ 7 int i,j,l,r; 8 int root[n+1][n+1]; 9 double e[n+2][n+2]; 10 double w[n+2][n+2]; 11 for(i=1;i<=n+1;i++){ 12 e[i][i-1]=0; 13 w[i][i-1]=0; 14 } 15 16 for(l=1;l<=n;l++){ 17 for(i=1; i<=n-l+1; i++){ // i,j between 1 and n+1,i是左边界,j是右边界 18 j=i+l-1; 19 e[i][j]=INF; 20 w[i][j]=w[i][j-1]+p[j]; 21 for(r=i;r<=j;r++){ 22 double temp=e[i][r-1]+e[r+1][j]+w[i][j]; 23 if(temp<e[i][j]){ 24 e[i][j]=temp; 25 root[i][j]=r; 26 } 27 } 28 } 29 } 30 printf(" e table "); 31 for(i=1;i<=n;i++){ 32 for(j=1;j<=n;j++){ 33 if(i>j)printf(" "); 34 else printf("%f ",e[i][j]); 35 } 36 printf(" "); 37 } 38 39 printf(" w table "); 40 for(i=1;i<=n;i++){ 41 for(j=1;j<=n;j++){ 42 if(i>j)printf(" "); 43 else printf("%f ",w[i][j]); 44 } 45 printf(" "); 46 } 47 48 printf(" root table "); 49 for(i=1;i<=n;i++){ 50 for(j=1;j<=n;j++){ 51 if(i>j)printf(" "); 52 else printf("%d ",root[i][j]); 53 } 54 printf(" "); 55 } 56 } 57 58 int main(){ 59 int n=5; 60 double p[6]={-1,0.25,0.2,0.05,0.2,0.3}; 61 Optimal_BST(p,n); 62 return 0; 63 }