最优二叉搜索树

二叉搜索树

是一棵空树或者满足以下的性质:

每个结点作为搜索对象,它的关键字是互不相同的。
对于树上的所有结点,如果它有左子树,那么左子树上所有结点的关键字都小于该结点的关键字。
对于树上的所有结点,如果它有右子树,那么右子树上所有结点的关键字都大于该结点的关键字。

搜索过程:

从根结点开始,如果根为空,则搜索不成功;否则使用待搜索值与根结点比较,如果待搜索值等于根结点关键字,则搜索成功返回,如果小于根结点,则向左子树搜索;如果大于根结点,则向右子树搜索。
对于一个给定的关键字集合,可能有若干不同的二分检索树
如对保留字的子集
Name: 1 2 3 4 5
for if loop repeat while
的两棵二分检索树为

 

 

 

考虑a图和b图中最坏比较次数和平均比较次数
构造不同的二叉搜索树就有不同的性能特征。
二叉搜索树a在最坏情况下找一个标识符需要4次比较,而b表示的二分检索树最坏情况下只需3次比较。
假设只作成功的检索并且检索每个标识符的概率相同,则两棵二分检索树在平均情况下各需要12/5和11/5次比较。

最优二叉搜索树

存在的两个问题
1 在实际中也会遇到不成功检索的情况。
2 在实际中,不同标识符会有不同的检索概率。
对给定的标识符集合,希望给出构造二分搜索树的方法,使得所构造的二分搜索树具有最优的性能。

在实际中也会遇到不成功检索的情况

扩充二叉树:当二叉树里出现空的子树时,就增加新的、特殊的结点——空树叶。

对于原来二叉树里度数为1的分支结点,在它下面增加一个空树叶;

对于原来二叉树的树叶,在它下面增加两个空树叶。

扩充二叉树是满二叉树,新增加的空树叶(以下称外部结点)的个数等于原来二叉树的结点(以下称内部结点)个数加1。

问题描述

设 S={x1, x2, ···, xn} 是一个有序集合,且x1, x2, ···, xn表示有序集合的二叉搜索树利用二叉树的顶点存储有序集中的元素,

而且具有性质:
存储于每个顶点中的元素x 大于其左子树中任一个顶点中存储的元素,小于其右子树中任意顶点中存储的元素。二叉树中的叶顶点是形如(xi, xi+1) 的开区间。
(1) 在二叉树的内部顶点处找到: x = xi
(2) 在二叉树的叶顶点中确定: x∈ (xi , xi+1)

 

 

 图例

代码

#include<bits/stdc++.h>
using namespace std;
const int M = 100;
double C[M][M], W[M][M], p[M], q[M];
int S[M][M];
int n, i, j, k;
void Optimal_BST()
{
    for (i=1;i<=n;i++)
    {
        C[i][i - 1] = 0.0;
        W[i][i - 1] = q[i - 1];
    }
    for (int t=1;t<=n;t++)
    {
        for (i = 1; i <= n - t + 1; i++)
        {
            j = i + t - 1;
            W[i][j] = W[i][j - 1] + p[j] + q[j];
            C[i][j] = C[i][i - 1] + C[i + 1][j];
            S[i][j] = i;
            // 选取i+1到j之间的某个下标的关键字作为i到j的根,如果组成的期望值最小
            // 则k为i到j的根结点
            for (k = i + 1; k < j; k++)
            {
                double tmp = C[i][k - 1] + C[k + 1][j];
                if (tmp<C[i][j] && fabs(tmp - C[i][j])>1E-6)
                {
                    C[i][j] = tmp;
                    S[i][j] = k;        // 记录i到j结点的树根
                }
            }
            C[i][j] += W[i][j];
        }
    }
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
    if (flag == 0)
    {
        cout << "S" << S[i][j] << "是根" << endl;
        flag = 1;
    }
    int k = S[i][j];
    // 如果左子树是叶子
    if (k - 1 < i)
    {
        cout << "e" << k - 1 << "is the left child of " << "S" << k << endl;
    }
    else
    {
        cout << "S" << S[i][k - 1] << " is the left child of" << "S" << k << endl;
        Construct_Optimal_BST(i, k - 1, 1);
    }
    // 如果右子树是叶子
    if (k>=j)
    {
        cout << "e" << j << " is the right child of " << "S" << k << endl;
    }
    else
    {
        cout << "S" << S[k + 1][j] << " is the right child of " << "S" << k << endl;
        Construct_Optimal_BST(k + 1, j, 1);
    }
}
int main()
{
    cout << "请输入关键字的个数:" << endl;
    cin >> n;
    cout << "请依次输入关键字的频率:" << endl;
    for (i = 1; i <= n; i++)
    {
        cin >> p[i];
    }
    cout << "请依次输入每个虚结点的搜索频率:" << endl;
    for (i = 0; i <= n; i++)
    {
        cin >> q[i];
    }
    Optimal_BST();
    cout << "最小的搜索成本是" << C[1][n] << endl;
    cout << "最优二叉搜索树为:" << endl;
    Construct_Optimal_BST(1, n, 0);
    system("pause");
    return 0;
}

 

posted @ 2022-11-15 23:23  kuailest  阅读(71)  评论(0编辑  收藏  举报