zoukankan      html  css  js  c++  java
  • 最优二叉查找树

    最优二叉树也就是哈夫曼树,最优二叉树和最优二叉查找树是不一样的。我们说一下他们的定义

    最优二叉树:

    给你n个节点,每一个节点有一个权值wi。我们设一棵树的权值是所有节点的权值乘于每一个节点的深度,但是我们可以构造出来许多二叉树,我们称构造出来的那个权值最小的二叉树就是我们找的最优二叉树

    求解最优二叉树:

    (1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

    (2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

    (3)从森林中删除选取的两棵树,并将新树加入森林;

    (4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

    最优二叉查找树:

    给定n个节点的值key,假设x是二叉搜索树中的一个结点。如果L是x的左子树的一个结点,那么L.key ≤ x.key。如果R是x的右子树的一个结点,那么R.key ≥ x.key。使用<key1,key2,key3....keyn>表示,且我们设定key1<key2<key3<keyn

    对于n个节点都有一个访问概率pi,使用<p1,p2,p3....pn>表示。还有未找到访问点概率qi,我们使用<q0,q1,q2,q3....qn>表示。

    例如访问到[-∞,key1)的概率是q0,访问到(key1,key2)的概率是q1,,,,访问到(keyn,)的概率是qn。

    我们设定[-∞,key1)区间为d0(key1,key2)区间为d1,,,,(keyn,∞)区间是dn。

    所以是不会出现对于i,j(1<=i,j<=n)满足keyi==keyj的情况出现

    我们需要把2*n+1个节点放在一个二叉树上,其中n个节点是keyi,还有n+1个节点di。

    最后形成的二叉树中叶节点肯定是di。且∑ni=1pi+∑ni=0qi=1

    假定一次搜索的代价等于访问的结点数,也就是此次搜索找到的结点在二叉搜索树中的深度再加1。给定一棵二叉搜索树T,我们可以确定进行一次搜索的期望代价如下:

    其中depthT​表示一个结点在二叉搜索树T中的深度。

    E = ∑ni=1 (depthT(keyi)+1)*pi  +  ∑ni=0(depthT(keyi)+1)*qi

     = 1 + ∑ni=1 (depthT(keyi))*pi  +  ∑ni=0(depthT(keyi))*qi

    我们要找到那个期望E最小的满足题意二叉树,这就是最优二叉查找树

    最优二叉树是符合最优子结构的,假设由关键字子序列<keyi,keyi+1,,,,keyj>和伪关键字子序列<di-1,di,di+1,,,,,dj>构成的一棵最优二叉搜索树以kr ( i ≤ r ≤ j )为根结点。那么它的左子树由子序列<keyi,,,,keyr-1>和<di-1,,,,dr-1>构成,这颗左子树显然也是一棵最优二叉搜索树。同样,它的右子树由子序列<keyr+1,,,,keyj>和<dr,,,,dj>构成,这颗右子树显然也是一棵最优二叉搜索树。

    注意一下空子树,也就是由关键字<keyi,,,,keyj>,且当选ki为根节点的时候,它的左子树<keyi,keyi-1>就只包含di-1,同理选kj为根节点时,右子树只包含dj。

    用e[i,j]表示包含关键字子序列<keyi,keyi+1,,,,keyj>的最优二叉搜索树的期望搜索代价。我们最终希望计算出e[1,n]。

    当j=i-1时,说明此时只有di-1,故e[i,i-1] = qi-1

    当j≥i时,需要从ki,……,kj中选择一个跟kr,然后用关键字ki,……,kr-1来构造一棵最优二叉查找树作为左子树,用关键字kr+1,……,kj来构造一棵最优二叉查找树作为右子树。定义一棵有关键字ki,……,kj的子树,定义概率的总和为:

    因此如果kr是一棵包含关键字ki,……,kj的最优子树的根,则有:

    整理得:

    最终递推式:


    e[i,j]给出了最优二叉搜索树子问题的期望搜索代价。我们还需要记录最优二叉搜索树子问题的根结点,用root[i,j]来记录。

    给出伪代码:

    代码:

    #include<iostream>
    using namespace std;
    
    const int MAX=9999999;
    //const int N=5;  //这是第一个例子
    //float p[N+1]={0,0.15,0.10,0.05,0.1,0.20};
    //float q[N+1]={0.05,0.10,0.05,0.05,0.05,0.10};
    const int N=7;    //这是第二个例子
    float p[N+1]={0.04 ,0.06, 0.08, 0.02, 0.10, 0.12, 0.14};
    float q[N+1]={0.06 ,0.06, 0.06, 0.06, 0.05, 0.05, 0.05,    0.05};
    
    float e[N+2][N+1];
    int root[N+1][N+1];
    float w[N+2][N+1];
    
    void optimal_bst_search_tree(float p[],float q[],int n)
    {
        int i;
        for(i=1;i<=n+1;i++)
        {
            e[i][i-1]=q[i-1];
            w[i][i-1]=q[i-1];
        }
        int l,j,r;
        for(l=1;l<=n;l++)  //枚举区间长度,也就是e[i][j]的j-i+1的长度
        {
            for(i=1;i<=n-l+1;i++)
            {
                j=i+l-1;
                e[i][j]=MAX;
                w[i][j]=w[i][j-1]+p[j]+q[j];
                for(r=i;r<=j;r++)
                {
                    double t=e[i][r-1]+e[r+1][j]+w[i][j];
                    if(t<e[i][j])
                    {
                        e[i][j]=t;
                        root[i][j]=r;
                    }
                }
            }
        }
    }
    
    void print_root()
    {
        int i,j;
        cout<<"各子树的根:"<<endl;
        for(i=1;i<=N;i++)
        {
            for(j=1;j<=N;j++)
                cout<<root[i][j]<<" ";
            cout<<endl;
        }
    }
    
    void construct_optimal_bst(int i,int j)
    {
        if(i<=j)
        {
            int r=root[i][j];
            cout<<r<<" ";
            construct_optimal_bst(i,r-1);
            construct_optimal_bst(r+1,j);
        }
    }
    void print_bst(int i,int j)
    {
        if(i==1&&j==N)
            cout<<"root is "<<root[i][j]<<endl;
        if(i<j)
        {
            int r=root[i][j];
            if(i!=r)
                cout<<"left child root "<<root[i][r-1]<<endl;
            print_bst(i,root[i][j]-1);
            if(j!=r)
                cout<<"right child root "<<root[r+1][j]<<endl;
            print_bst(root[i][j]+1,j);
        }
    }
    int main()
    {
        optimal_bst_search_tree(p,q,N);
        print_root();
        cout<<"构造的最优二叉树:"<<endl;
        construct_optimal_bst(1,5);
        cout<<endl;
        print_bst(1,N);
        return 0;
    }
    //0.04 ,0.06, 0.08, 0.02, 0.10, 0.12, 0.14
    //0.06 0.06 0.06 0.06 0.05 0.05 0.05    0.05

    第二个例子的最优二叉查找树如下:算出来期望是3.12

  • 相关阅读:
    全屏后无法检查键值
    根据秒转化为周天时秒分,不足补0
    日期时间格式化(Date对象)
    AS3 RGB颜色
    Egret eui中eui.ItemRenderer类型的组件在渲染界面时,dataChanged()重复调用
    egret 画圆 画圆角矩形 画矩形
    egert 缓动
    Flashdeveloper开多个实例进程解决方案
    flash 发布exe文件
    sublime Text3 如何自动排版代码
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/14073783.html
Copyright © 2011-2022 走看看