原问题是给出各个节点和各个节点的被查找概率,然后构造一棵各个节点平均被查找比较次数最小的树,则该问题可以用动态规划来解决

最优二叉查找树_动态规划_最小值

示例如下

最优二叉查找树_动态规划_i++_02

推广到一般的情况,并设T(i, j)是由记录{ri, …, rj}(1≤i≤j≤n)构成的二叉查找树,C(i, j)是这棵二叉查找树的平均比较次数,有下列分析

最优二叉查找树_动态规划_查找树_03

最优二叉查找树_动态规划_最小值_04

最优二叉查找树_动态规划_i++_05

最优二叉查找树_动态规划_最小值_06

最优二叉查找树_动态规划_i++_07

最优二叉查找树_动态规划_最小值_08

观察这个表,可知可知左边的表的第一行的第四列就是我们要求的最优平均比较次数,而右边的表我们可以知道在c(i ,j)得到最优解,即平均查找次数最小的根节点,比如一共四个节点,则我们从右边的R(1,4)的值即3是这四个节点构成的树的根节点。则树的左子树变为c(1,2),他的根节点是r(1,2)=2,然后2又有左节点1,而4则是3的根节点。则树的样子便出来了。

代码如下

1 #include<bits/stdc++.h>
2 using namespace std;
3 double BST(int n,double p[],double c[][100],int r[][100])
4 {
5 for(int i=1;i<=n;i++){//按式1和式2初始化
6 c[i][i-1]=0;
7 c[i][i]=p[i];
8 r[i][i]=i;
9 }
10 c[n+1][n]=0;
11 for(int d=1;d<n;d++){//安对角线计算,此时是n-1个对角线
12 for(int i=1;i<=n-d;i++){//行的取值范围
13 int j=i+d;//求出在对角线上的i对应的j
14 double minnum=99999.0;//出是一个较大的值
15 int mink=i;
16 double sum=0;
17 for(int k=i;k<=j;k++)
18 {
19 sum=sum+p[k];
20 if(c[i][k-1]+c[k+1][j]<minnum){//不断比较,获取最小的值
21 minnum=c[i][k-1]+c[k+1][j];
22 mink=k;
23 }
24 }
25 c[i][j]=minnum+sum;//得到了最小值
26 r[i][j]=mink;//记录取得最小值时的根节点
27 }
28
29 }
30 return c[1][n];
31 }
32 int main()
33 {
34 cout << "请输入树的节点的个数" << endl;
35 int n;
36 cin >> n;
37 cout << "请输入每个节点的被查找概率" << endl;
38 double p[n];
39 memset(p,0,sizeof(p));
40 for(int i=1;i<=n;i++)
41 {
42 cin >> p[i];
43 }
44 double c[n+2][100];
45 int r[n+2][100];
46 memset(r,0,sizeof(r));
47 memset(c,0,sizeof(c));
48 double s=BST(n,p,c,r);
49 cout << "最小平均比较次数为" << s<<endl;
50 cout << "平均最小概率矩阵如下:" << endl;
51 for(int i=1;i<=n+1;i++){
52 for(int j=0;j<=n;j++){
53 cout << c[i][j] << " ";
54 }
55 cout << endl;
56 }
57
58 cout << "最优二叉查找树对应的根节点 " << endl;
59 for(int i=1;i<=n+1;i++){
60 for(int j=0;j<=n;j++){
61 cout << r[i][j] << " ";
62 }
63 cout << endl;
64 }
65
66 return 0;
67 }

运行结果如下

最优二叉查找树_动态规划_最小值_09

具体树的构造我们可以从数组r求出,等我有空再把代码补上,用程序把树的构造描绘出来

作者:你的雷哥

本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。