动态规划方法生成最优二叉查找树
转自:http://www.cnblogs.com/lpshou/archive/2012/04/26/2470914.html
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,加入一个节点
an 后要求构成规模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(算法设计与分析,第八章)
另附:http://blog.sina.com.cn/s/blog_51cea4040100gk6v.html
动态规划求0-1背包问题
1.问题描述如下:
经过分析后算法如下:v(i,j)表示前j个物品中能够放入承重量为j的包中的最大价值。。。。
2.具体的代码实现如下:
其中2.txt中的具体数据为:
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 #include<stdio.h> 2 #include<stdlib.h> 3 void maxValue(int *,int *,int **,int,int); 4 int ziji(int *,int *,int *,int **,int,int); 5 int max(int,int); 6 void main() 7 { 8 int i,k,num,weight; 9 FILE *p; 10 p=fopen("2.txt","r"); 11 if(p==NULL) 12 { 13 printf("文件打开失败!"); 14 exit(1); 15 } 16 fscanf(p,"%d%d",&num,&weight); 17 int *w=(int*)malloc(sizeof(int)*num); 18 int *v=(int*)malloc(sizeof(int)*num); 19 int *path=(int*)malloc(sizeof(int)*num); 20 int **vv=(int **)malloc(sizeof(int *)*(num+1)); 21 for(i=0;i<num+1;i++) 22 vv[i]=(int *)malloc(sizeof(int)*(weight+1)); 23 for(i=1;i<=num;i++) 24 fscanf(p,"%d",&w[i]); 25 for(i=1;i<=num;i++) 26 fscanf(p,"%d",&v[i]); 27 28 /*printf("%d %d\n",num,weight); 29 for(i=0;i<num;i++) 30 printf("%d\n",w[i]); 31 for(i=0;i<num;i++) 32 printf("%d\n",v[i]);*/ 33 maxValue(w,v,vv,num,weight); 34 printf("最终最大价值为: %d\n",vv[num][weight]); 35 k=ziji(w,v,path,vv,num,weight); 36 printf("最优子集为: \n"); 37 for(i=0;i<k;i++) 38 printf("%d ",path[i]); 39 } 40 //求最终最大的价值 41 void maxValue(int *w,int *v,int **vv,int num,int weight) 42 { 43 int i,j,k; 44 for(i=0;i<=num;i++) 45 vv[i][0]=0; 46 for(i=0;i<=weight;i++) 47 vv[0][i]=0; 48 for(i=1;i<=num;i++) 49 for(j=1;j<=weight;j++) 50 { 51 if(j>=w[i]) 52 { 53 k=j-w[i]; 54 vv[i][j]=max((v[i]+vv[i-1][k]),vv[i-1][j]); 55 } 56 else 57 vv[i][j]=vv[i-1][j];//谢谢楼下的提醒,此处已更正。。 58 } 59 } 60 //求最优子集 61 int ziji(int *w,int *v,int *path,int **vv,int num,int weight) 62 { 63 int i,j=5,k=0; 64 for(i=num;i>0;i--) 65 { 66 if(j>0) 67 { 68 if(vv[i][j]!=vv[i-1][j]) 69 { 70 path[k++]=i; 71 j=j-w[i]; 72 } 73 } 74 } 75 return k; 76 } 77 int max(int x,int y) 78 { 79 if(x>y) return x; 80 else return y; 81 }
附:
另外附两幅网上找到的ppt,以便于理解,参考:http://wenku.baidu.com/view/b9e4f70416fc700abb68fce0.html
(1)最大价值v(4,5)
(2)回溯法找到最优子集
动态规划求数塔问题
问题描述如下:
具体解决方案如下:
{ 其中2.txt中保存的数据为:
}
1 #include<stdio.h> 2 #include<stdlib.h> 3 int m; 4 void dqta(int **data,int **d); 5 int max(int,int); 6 void fpath(int **data,int **d,int *path); 7 void main() 8 { 9 int i,j; 10 FILE *p; 11 p=fopen("2.txt","r"); 12 if(p==NULL) 13 { 14 printf("读取文件失败!\n"); 15 exit(1); 16 } 17 else 18 fscanf(p,"%d",&m); 19 //创建动态数组; 20 int **data=(int **)malloc(sizeof(int *)*m); 21 for(i=0;i<m;i++) 22 data[i]=(int *)malloc(sizeof(int)*m); 23 int **d=(int **)malloc(sizeof(int *)*m); 24 for(i=0;i<m;i++) 25 d[i]=(int *)malloc(sizeof(int)*m); 26 27 int *path=(int *)malloc(sizeof(int)*m); 28 //读取文件中的数据; 29 for(i=0;i<m;i++) 30 for(j=0;j<=i;j++) 31 fscanf(p,"%d",&data[i][j]); 32 dqta(data,d); 33 printf("最大值为:%d\n",d[0][0]); 34 fpath(data,d,path); 35 printf("最终路径为:\n"); 36 for(i=0;i<m;i++) 37 printf("%d\n",path[i]); 38 } 39 int max(int x,int y) 40 { 41 if(x>y)return x; 42 else return y; 43 } 44 void dqta(int **data,int **d) 45 { 46 int i,j; 47 for(j=0;j<m;j++) 48 d[m-1][j]=data[m-1][j]; 49 for(i=m-2;i>=0;i--) 50 for(j=0;j<=i;j++) 51 d[i][j]=max(d[i+1][j],d[i+1][j+1])+data[i][j]; 52 } 53 void fpath(int **data,int **d,int *path) 54 { 55 int i,j,b; 56 path[0]=data[0][0]; 57 j=0; 58 for(i=1;i<m;i++) 59 { 60 b=d[i-1][j]-data[i-1][j]; 61 if(b==d[i][j]) 62 { 63 path[i]=data[i][j]; 64 } 65 else 66 { 67 path[i]=data[i][j+1]; 68 j++; 69 } 70 } 71 }
动态规划求有向无环图的单源最短路径
问题描述如下:
具体代码实现:
1 #include<stdlib.h> 2 #include<stdio.h> 3 #define x 9999 4 #define max 9999 5 int data[10][10]; 6 int dist[10];//记录最短路径为多少 7 int path[10];//记录最短路径 8 int kmin(int,int); 9 void fpath(int a[][10]); 10 int froute(int a[][10]); 11 void main() 12 { 13 int i,m; 14 int a[10][10]={ 15 {x,4,2,3,x,x,x,x,x,x}, 16 {x,x,x,x,10,9,x,x,x,x}, 17 {x,x,x,x,6,7,10,x,x,x}, 18 {x,x,x,x,x,3,8,x,x,x}, 19 {x,x,x,x,x,x,x,4,8,x}, 20 {x,x,x,x,x,x,x,9,6,x}, 21 {x,x,x,x,x,x,x,5,4,x}, 22 {x,x,x,x,x,x,x,x,x,8}, 23 {x,x,x,x,x,x,x,x,x,4}, 24 {x,x,x,x,x,x,x,x,x,x}}; 25 26 /*for (i=0;i<10;i++) 27 { 28 for(j=0;j<10;j++) 29 printf("%d ",a[i][j]); 30 printf("\n"); 31 }*/ 32 fpath(a); 33 printf("最短路径大小为: %d\n",dist[9]); 34 35 m=froute(a); 36 for(i=m-1;i>=0;i--) 37 printf("最短路径经过: %d\n",path[i]); 38 } 39 void fpath(int a[][10]) 40 { 41 int i,j,k; 42 dist[0]=0; 43 for(i=1;i<10;i++) 44 { 45 k=max; 46 for(j=0;j<i;j++) 47 { 48 if(a[j][i]!=x) 49 if((dist[j]+a[j][i])<k) 50 k=dist[j]+a[j][i]; 51 } 52 dist[i]=k; 53 } 54 } 55 int froute(int a[][10]) 56 { 57 int j,b,k=1,i=9; 58 path[0]=10; 59 while(i>0) 60 { 61 for(j=i-1;j>=0;j--) 62 { 63 if(a[j][i]!=x) 64 { 65 b=dist[i]-a[j][i]; 66 if(b==dist[j]) 67 { 68 path[k++]=j+1; 69 i=j; 70 break; 71 } 72 } 73 74 } 75 } 76 return k; 77 } 78 79 80