动态规划最优二叉搜索树

前面的optimal-bst还能写出来,要去construct就不会了,也是理解不够深刻的原因吧。以下代码的construct用了两种方法,参照了两位博主,前一个代码风格非常暴力,能看懂以后写不出来系列。第二种的d的输出那我没有看出逻辑,好像是用的输出的相邻两个下标是在同一个节点上连着?这个的理由我没想出来(我也不确定该博主代码是否正确,但输出是对的)。
参照 https://blog.csdn.net/c18219227162/article/details/50429597
我在看最优二叉搜索树的时候遇到一点问题(现在也觉得没有很透彻理解这个)

e[i][j]的递归式我就看了好一会…..首先联系最开始提出的英语翻译成法语,如果搜索的内容没有,也就是书上的d0,d1,d2……,做的处理就是在二维数组e[i][j]中j=i-1,为什么搜索的期望是q[i-1]呢,当j=i-1的时候表示的是没有真实关键字,只有虚拟键,那深度就是0,再加一,乘以概率最后就是概率了。当i<=j的时候,我开始就没想通为什么后面加个w(i,j),w(i,j)是表示的某子树的概率和,e[i][j]表示的是期望。在算法里面,我们是在找root也就是kr,找到root之后,为这棵树构造一个最优的左子树和最优的右子树,也就是在左边和右边都还要再找一个root,构造成左子树和右子树,这个时候子树的搜索代价就变了,子树中的每个节点深度都增加了1(一棵树变成一个结点的子树时深度+1)。结合搜索的期望的公式,(深度+1)*p+(深度+1)*q那个公式,可以看出最后就是多了一个概率和。左子树的e加上右子树的e加上一个w就行了。

还有就是写construct的时候,不知道怎么从一个root得到他的左子树和右子树是什么。
printf(“k%d是k%d的左孩子\n”,root[i][root[i][j]-1],root[i][j]);
printf(“k%d是k%d的右孩子\n”, root[root[i][j] + 1][j], root[i][j]);
其实这一点,看递推公式那里就能看出来。


#include "stdafx.h"

#include
#define n 5
int root[n+1][n+1];                              //树根
double e[n + 2][n + 2];                     //e是当前的搜索期望
double w[n + 2][n + 2];                      //w是每个子树上的概率和
double p[n+1] = { -1,0.15,0.1,0.05,0.10,0.20 };             //关键字的概率
double q[n+1] = { 0.05,0.10,0.05,0.05,0.05,0.10 };             //虚拟键的概率
void OPTIMAL_BST(double *p, double  *q)
{
    int i,l,r,j;
    double t;
    for (i = 1; i <= n + 1; i++)          //当j=i-1就是虚拟键的情况
    {
        e[i][i - 1] = q[i - 1];
        w[i][i - 1] = q[i - 1];
    }
    for (l = 1; l <= n; l++)       
    {
        for (i = 1; i <= n - l + 1; i++)
        {
            j = i + l - 1;                        
            e[i][j] = 0x7fffffff;                 //先赋值无穷
            w[i][j] = w[i][j - 1] + p[j] + q[j];     //w递归 ,注意pq的下标相同,也就是假如当用到k2,d2时他们调用取得是相同的下标,所以p,q长度不同把p前面弄了一个-1
            for (r = i; r <= j; r++)                            // r代表的是k或d的下标
            {
                t = e[i][r - 1] + e[r + 1][j] + w[i][j];         //e[i][j]的递归
                if (t < e[i][j])
                {
                    e[i][j] = t;                                 //e[i][j]取最小值
                    root[i][j] = r;                              //当代价取到最小值的时候,i到j的根存在root[i][j]中
                }
            }
        }
    }
}

/*

void  CONSTRUCT_OPTIMAL_BST(int r, int i, int j)
{
    int child = root[i][j];
    if (child == root[1][n])
    {
        printf("整棵树的根是:k%d\n", root[1][n]);                              //1到n是整棵树的根
        CONSTRUCT_OPTIMAL_BST(child, i, child - 1);
        CONSTRUCT_OPTIMAL_BST(child, child + 1, j);
        return;                                            //return;在void里面强制退出,可设置断点跟踪看清用法
    }

    if (j < i - 1)
        return ;
    else    if (j == i - 1)                //虚拟键
    {
        if (j < r)
            printf("d%d是k%d的左孩子\n", j, r);
        else
            printf("d%d是k%d的右孩子\n", j, r);
        return ;
    }
    else                                   //关键字
    {
        if (child < r)
            printf("k%d是k%d的左孩子\n", child, r);
        else
            printf("k%d是k%d的右孩子\n", child, r);
    }
    CONSTRUCT_OPTIMAL_BST(child,i,child - 1);
    CONSTRUCT_OPTIMAL_BST(child,child+1,j);

}

*/

void CONSTRUCT_OPTIAML_BST(int root[][n+1], int i, int j)
{
    if(i==1&&j==n)
        printf("整棵树的根是:k%d\n", root[1][n]);
    if (i < j)
    {
        printf("k%d是k%d的左孩子\n",root[i][root[i][j]-1],root[i][j]);
        CONSTRUCT_OPTIAML_BST(root, i, root[i][j] - 1);
        if (root[i][j] + 1 < j)
            printf("k%d是k%d的右孩子\n", root[root[i][j] + 1][j], root[i][j]);
        CONSTRUCT_OPTIAML_BST(root, root[i][j] + 1, j);
    }
    if (i == j)
    {
        printf("d%d是K%d的左孩子\n", i - 1, i);
        printf("d%d是K%d的右孩子\n", i, i);
    }
    if (i > j)
        printf("d%d是K%d的右孩子\n", j, j);

}



int main()
{
    OPTIMAL_BST(p, q);
    //CONSTRUCT_OPTIMAL_BST(-1, 1, n);
    CONSTRUCT_OPTIAML_BST(root, 1, n);
}

你可能感兴趣的:(算法导论(C语言实现))