最优二叉搜索树

最优二叉搜索树

问题:

给出一系列节点,以及虚节点(不在树中的数值区间)的概率,确定一个平均查找路径最短的二叉搜索树。

在这里插入图片描述

1)最优子结构

说明这问题之前,先引入一些符号的说明:

a i a_i ai代表失败节点发生的概率
b i b_i bi代表节点发生的概率
T ( i , j ) T(i,j) T(i,j)是由节点 a i − 1 a_{i-1} ai1, a i a_{i} ai, b i b_{i} bi,… a j a_{j} aj, b j b_{j} bj构成的查找树

假设一棵二叉查找树的最优查找树为 T ( i , j ) T(i,j) T(i,j),对应的,最短平均路径为P。以k为跟节点,能拆分为 T ( i , k − 1 ) T(i,k-1) T(i,k1) T ( k + 1 , j ) T(k+1,j) T(k+1,j)两棵子树,对应的最短平均路径为 P l P_l Pl, P r P_r Pr
对于 T ( i , k − 1 ) T(i,k-1) T(i,k1),如果其不是最优查找树,则存在一个 P l ′ P_{l'} Pl< P l P_l Pl,使得这样T(i,j)能够得到更优的查找树,其平均路径P’<P,与 T ( i , j ) T(i,j) T(i,j)是最优查找树矛盾。

所以一棵最优查找树,其子树也需要为最优的结构。

2)状态转移方程

在考虑动态规划的方案之前,当然先得考虑子问题的规模。在子规模能够接受的范围内,才考虑使用动态规划的方法。

在这个问题当中,子问题相对好确定,关键还是状态转移处的问题,这里就先把这问题说清楚。

w ( i , j ) w(i,j) w(i,j)= Σ i − 1 j a i \Sigma_{i-1}^ja_i Σi1jai+ Σ i j b i \Sigma_i^jb_i Σijbi,节点概率之和。

p ( i , j ) p(i,j) p(i,j)代表树 T ( i , j ) T(i,j) T(i,j)的最短平均查找路径

对于一棵树 T ( i , j ) T(i,j) T(i,j),假定以k为根节点,能够得到一棵最优查找树,则其 p ( i , j ) p(i,j) p(i,j)可以拆解为:

p ( i , j ) p(i,j) p(i,j)= b k w ( i , j ) ∗ 1 + w ( i , k − 1 ) w ( i , j ) ∗ ( p ( i , k − 1 ) + 1 ) + w ( k + 1 , j ) w ( i , j ) ∗ ( p ( k + 1 , j ) + 1 ) \frac{b_k}{w(i,j)}*1+\frac{w(i,k-1)}{w(i,j)}*(p(i,k-1)+1)+\frac{w(k+1,j)}{w(i,j)}*(p(k+1,j)+1) w(i,j)bk1+w(i,j)w(i,k1)(p(i,k1)+1)+w(i,j)w(k+1,j)(p(k+1,j)+1)

其中,

b k w ( i , j ) \frac{b_k}{w(i,j)} w(i,j)bk:指以k节点作为根节点的概率,是一个条件概率。

b k w ( i , j ) \frac{b_k}{w(i,j)} w(i,j)bk*1:指以k节点作为根节点的平均查找长度,根节点查找长度为1。

w ( i , k − 1 ) w ( i , j ) \frac{w(i,k-1)}{w(i,j)} w(i,j)w(i,k1):表示在T(i,j)条件下,T(i,i-k)作为一个子树的概率。

p ( i , k − 1 ) + 1 p(i,k-1)+1 p(i,k1)+1:由于以k节点为根节点,T(i,i-k)所有节点深度+1,T(i,i-k)的最短平均查找路径+1。

w ( i , k − 1 ) w ( i , j ) ∗ ( p ( i , k − 1 ) + 1 ) \frac{w(i,k-1)}{w(i,j)}*(p(i,k-1)+1) w(i,j)w(i,k1)(p(i,k1)+1):表示在当前的划分下,左子树的最短平均查找长度。右子树同理。

对等式化简:
p ( i , j ) p(i,j) p(i,j)= b k w ( i , j ) ∗ 1 + w ( i , k − 1 ) w ( i , j ) ∗ ( p ( i , k − 1 ) + 1 ) + w ( k + 1 , j ) w ( i , j ) ∗ ( p ( k + 1 , j ) + 1 ) \frac{b_k}{w(i,j)}*1+\frac{w(i,k-1)}{w(i,j)}*(p(i,k-1)+1)+\frac{w(k+1,j)}{w(i,j)}*(p(k+1,j)+1) w(i,j)bk1+w(i,j)w(i,k1)(p(i,k1)+1)+w(i,j)w(k+1,j)(p(k+1,j)+1)

p ( i , j ) p(i,j) p(i,j)= b k + w ( i , k − 1 ) + w ( k + 1 , j ) w ( i , j ) + w ( i , k − 1 ) w ( i , j ) ∗ p ( i , k − 1 ) + w ( k + 1 , j ) w ( i , j ) ∗ p ( k + 1 , j ) \frac{b_k+w(i,k-1)+w(k+1,j)}{w(i,j)}+\frac{w(i,k-1)}{w(i,j)}*p(i,k-1)+\frac{w(k+1,j)}{w(i,j)}*p(k+1,j) w(i,j)bk+w(i,k1)+w(k+1,j)+w(i,j)w(i,k1)p(i,k1)+w(i,j)w(k+1,j)p(k+1,j)

p ( i , j ) = w ( i , j ) w ( i , j ) + w ( i , k − 1 ) w ( i , j ) ∗ p ( i , i − k ) + w ( k + 1 , j ) w ( i , j ) ∗ p ( k + 1 , j ) p(i,j)=\frac{w(i,j)}{w(i,j)}+\frac{w(i,k-1)}{w(i,j)}*p(i,i-k)+\frac{w(k+1,j)}{w(i,j)}*p(k+1,j) p(i,j)=w(i,j)w(i,j)+w(i,j)w(i,k1)p(i,ik)+w(i,j)w(k+1,j)p(k+1,j)

p ( i , j ) ∗ w ( i , j ) = w ( i , j ) + w ( i , k − 1 ) ∗ p ( i , k − 1 ) + w ( k + 1 , j ) ∗ p ( k + 1 , j ) p(i,j)*w(i,j)=w(i,j)+w(i,k-1)*p(i,k-1)+w(k+1,j)*p(k+1,j) p(i,j)w(i,j)=w(i,j)+w(i,k1)p(i,k1)+w(k+1,j)p(k+1,j)

实际上,k这点是需要确认的,需要比较所有可能的情况,所以上述式子为:

p ( i , j ) ∗ w ( i , j ) = m i n i ⩽ k ⩽ j ( w ( i , j ) + w ( i , k − 1 ) ∗ p ( i , k − 1 ) + w ( k + 1 , j ) ∗ p ( k + 1 , j ) ) p(i,j)*w(i,j)=min_{i\leqslant k\leqslant j}(w(i,j)+w(i,k-1)*p(i,k-1)+w(k+1,j)*p(k+1,j)) p(i,j)w(i,j)=minikj(w(i,j)+w(i,k1)p(i,k1)+w(k+1,j)p(k+1,j))
                 = w ( i , j ) + m i n i ⩽ k ⩽ j ( w ( i , k − 1 ) ∗ p ( i , k − 1 ) + w ( k + 1 , j ) ∗ p ( k + 1 , j ) ) =w(i,j)+min_{i\leqslant k\leqslant j}(w(i,k-1)*p(i,k-1)+w(k+1,j)*p(k+1,j)) =w(i,j)+minikj(w(i,k1)p(i,k1)+w(k+1,j)p(k+1,j))

不妨令 m ( i , j ) = p ( i , j ) ∗ w ( i m(i,j)=p(i,j)*w(i m(i,j)=p(i,j)w(i,j),则进一步化为:

m ( i , j ) = w ( i , j ) + m i n i ⩽ k ⩽ j ( m ( i , k − 1 ) + m ( k + 1 , j ) ) m(i,j)=w(i,j)+min_{i\leqslant k\leqslant j}(m(i,k-1)+m(k+1,j)) m(i,j)=w(i,j)+minikj(m(i,k1)+m(k+1,j))

一开始看递推关系式的时候,比较容易迷惑的是,子树高度发生了改变,但是关系式中似乎没有体现出来?从推导过程看出,每次的增加的高度其实都“合并”到w(i,j)当中去了。

3) 子问题

在动态规划中,要求,每一步进行计算的时候,子问题都已经得到解决。

从上一部分的讨论中,我们可以看到,子问题可以按照如下图的方式来进行规划:

m(1,1)->m(2,2)->…->m(n,n)->…->m(1,2)->m(2,3)->…->m(1,n)
在这里插入图片描述

4)代码实现

对于包含了从i到j节点的树T(i,j):

w数组是保存的T(i,j)的所有节点的概率之和
m数组是保存T(i,j)的平均最短路径
s数组是保存T(i,j)平均最短路径对应的根节点的编号

#include <vector>
#include <string>

void OBSTa(std::vector<double> &a,std::vector<double> &b,int n ,std::vector<std::vector<double>> &m,std::vector<std::vector<double>> &s,std::vector<std::vector<double>> &w)
 {
    for (int i=0;i<=n;i++)
    {
        w[i+1][i]=a[i];
        m[i+1][i]=0;
    }
    for (int r=0;r<n;r++) //node number in array
        for (int i=1;i<=n-r;i++) {//head of array
            int j=i+r; //tail of array
            w[i][j]=w[i][j-1]+a[j]+b[j-1];
            m[i][j]= m[i+1][j];
            s[i][j]=i;
            for (int k=i+1;k<=j;k++) {//find the smallest m[i][j]
                double t=m[i][k-1]+m[k+1][j];
                if (t<m[i][j])
                {
                    m[i][j]=t;
                    s[i][j]=k;
                }
            }
            m[i][j]+=w[i][j];
        }
}

//根据s结果,构建并查集
void unionFind(int i,int j,int root,std::vector<std::vector<double>> &s,std::vector<int> &result){
    int newRoot=s[i][j];
    result[newRoot]=root;
    if(i<newRoot)
        unionFind(i,newRoot-1,newRoot,s,result);
    if(newRoot<j)
        unionFind(newRoot+1,j,newRoot,s,result);
}


int main(){

    std::vector<double> b(9,0.06);
    std::vector<double> a{0.1,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04};

    int n=b.size();
    std::vector<std::vector<double>> m(a.size()+1,std::vector<double>(a.size(),0));
    std::vector<std::vector<double>> s(a.size(),std::vector<double>(a.size(),0));
    std::vector<std::vector<double>> w(a.size()+1,std::vector<double>(a.size(),0));
    OBSTa(a,b,n,m,s,w);
    std::vector<int> array(b.size()+1,0);
    unionFind(1,b.size(),-1,s,array);
    printf("Union Find of BST:\nNode Number:");
    for(int i=1;i<b.size()+1;i++)
        printf("%2d  ",i);
    printf("\nValue      :");
    for(int i=1;i<b.size()+1;i++)
        printf("%2d  ",array[i]);

    printf("\n");
    printf("\nShortest average search length:%5.3f",m[1][b.size()]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值