问题描述:
最优二叉搜索树问题是对于有序集S及其存取概率或权值(q0,p1,q1,p2,q2,p3,q3),在所有表示有序集S的二叉搜索树中找出一颗具有最小平均路长的二叉搜索树。
解决方法:
使用动态规划方法自底向上逐步构造最优二叉搜索树。
动态规划的两个重要要素是:
1、最优子结构。2、重叠子问题。
1)所谓最优化子结构是说若问题的一个最优解中包含了子问题的最优解,则该问题具有最优子结构。
2)重叠子问题,是指在递归解决方案中,若产生了大量的相同子问题,那么相同的子问题就会被重复计算很多次,这样算法的效率损耗就很大。
问题中,q0,q1,q2,q3为外部节点,即搜索不成功的权值或概率,p1,p2,p3为内部节点,即搜索成功的权值或概率,T[i][j]为一颗从i到j的子树,w[i][j]来存放累计权值,c[i][j]为一颗树的代价,r[i][j]存放一棵树的根。
<1>、算法思路:
(1)、构造只有一个内部节点的最优二叉搜索树:
c[i-1][j]=w[i-1][i]
r[i-1][i]=i
(2)、构造具有m(m>=2)个内部节点的最优二查搜索树:
c[i][j]=w[i][j]+min{c[i][k-1]+c[k][j]},i<k<=j
r[i][j]=k,i<k<=j
<2>、算法代码:
#include<iostream>
using namespace std;
const int n = 3;
int r[n+1][n+1];//存放各最优二叉树搜索树的根
int c[n+1][n+1];//最优二查搜索树的代价(最小平均路长)
int w[n+1][n+1];//存放内部节点外部节点的累计权值
void OBST(int *p,int *q,int n);
void printOBST(int r[n+1][n+1],int i,int j,int n,int f,char ch);
int main()
{
int p[]={0,5,10,50};//内部节点的权值
int q[]={15,10,5,5};//外部节点的权值
cout<<"内部节点的权值为:"<<endl;
for(int i=1;i<n+1;i++)//输出内部节点的权值
{
cout<<"p"<<i<<"="<<p[i]<<endl;
}
cout<<"外部节点的权值为:"<<endl;
for(int j=0;j<n+1;j++)//输出外部节点的权值
{
cout<<"q"<<j<<"="<<q[j]<<endl;
}
cout<<endl;
OBST(p,q,n);
cout<<"最优二叉搜索树的最小平均路长为:"<<c[0][n]<<endl;
cout<<"构造的最优二叉搜索树为:"<<endl;//输出最优二叉树搜索树各个子树的根
printOBST(r,0,n,n,0,'0');
system("pause");
return 0;
}
void OBST(int *p,int *q,int n)//自底向上逐步构造w[][],c[][],r[][]
{
int i,j,k,m,min,u;
for(i=0;i<n;i++){
//初始化
w[i][i]=q[i];
c[i][i]=r[i][i]=0;
//构造只有一个内部节点的最优二查搜索树
w[i][i+1]=w[i][i]+p[i+1]+q[i+1];
r[i][i+1]=i+1;
c[i][i+1]=w[i][i+1];
}
w[n][n]=q[n];
r[n][n]=c[n][n]=0;
for(m=2;m<=n;m++)//构造具有m个内部节点的最优二查搜索树
{
for(i=0;i<=n-m;i++){
//在前一颗树的基础上加以内部节点和一外部节点
//分别构造具有m,m+1...n个内部节点的最优二查搜索树
j=i+m;
w[i][j]=w[i][j-1]+p[j]+q[j];//构造出从i到j的累计权值
min=c[i+1][j];
u=i+1;//假定i+1为根
for(k=i+2;k<=j;k++)//轮流以i+2,i+3...j为根,选代价最小的送min,其根为u
{
if(c[i][k-1]+c[k][j]<min)
{
min=c[i][k-1]+c[k][j];
u=k;
}
}
c[i][j]=w[i][j]+min;
r[i][j]=u;
}
}
}
void printOBST(int r[n+1][n+1],int i,int j,int n,int f,char ch)
{
int k=r[i][j];
if(k>0)
{
if(f==0)
{
cout<<"T("<<i<<","<<j<<")"<<"的根节点为:"<<k<<endl;
}
else
{
cout<<ch<<"子树:"<<"T("<<i<<","<<j<<")"<<"的根节点为:"<<k<<endl;
}
int t=k-1;
if(t>=i && t<=n)
{
printOBST(r,i,t,n,k,'L');//递归输出左子树的根节点
}
int m=k+1;
if(t<=j)
{
printOBST(r,m-1,j,n,k,'R');//递归输出右子树的根节点
}
}
}