最优二叉查找树(optimal BST)

/**
    最优二叉查找树:一棵有n个结点的二叉查找树,已知每个结点的查找概率Pi(且∑Pi=1),要使查找操作的平均比较次数最小。(这里讨论的是成功查找,不讨论不成功的查找)
动态规划:
    c[i][j]表示由结点i~j组成的BST成功查找的最小平均查找次数。
    r[i][j]表示由结点i~j构成最优二叉查找树时的树根结点。
转换公式:
    c[i][j] = Min[i<=k<=j]{c[i,k-1] + c[k+1,j]} + Sum{Pi...Pj};
时间复杂度是O(n^3)
优化:把原来的树根可能是i->j中的任何一个,优化成r[i,j-1] <= r[i,j] <= r[i+1,j]
可以证明优化后的时间复杂度是O(n^2)
**/

const int MAXN = 1000 + 5;

double c[MAXN][MAXN];
int    r[MAXN][MAXN];

double optimalBST(int n, double p[])
{
    double sum[MAXN] = {0};
    for (int i = 1; i <= n; i++)    // 求前i项和是为了简化后面的计算
        sum[i] = sum[i-1] + p[i];
    if ( fabs(sum[n] - 1.0) > 1e-6 ) // 检验∑Pi=1
        return -1.0;
    for (int i = 1; i <= n; i++)  // initialize
    {
        c[i][i-1] = 0;
        c[i][i] = p[i];
        r[i][i] = i;
    }
    c[n+1][n] = 0;
    for (int d = 1; d < n; d++)
    {
        for (int i = 1; i <= n-d; i++)
        {
            int    j = i + d;
            int    root = -1;
            double minv = 1e9;

            for (int k = r[i][j-1]; k <= r[i+1][j]; k++) // k = i -> j 的优化
            {
                if (minv > c[i][k-1] + c[k+1][j])
                {
                    minv = c[i][k-1] + c[k+1][j];
                    root = k;
                }
            }
            r[i][j] = root;
            c[i][j] = minv + sum[j] - sum[i-1];
        }
    }
    return c[1][n];
}

你可能感兴趣的:(c,优化)