最优二叉查找树的详细分析,c++代码实现

首先看下关于最优二叉查找树的描述

给定一个由n个互异的关键字组成的序列K={k1,k2,…,kn},且关键字有序,对于每一个关键字ki,一次搜索为ki的概率是pi。某些搜索的值可能不在K内,因此还有n+1个虚拟键d0,d1,…,dn代表不再K内的值。d0代表所有小于k1的值,dn代表所有大于kn的值,对于i=1,2,…,n-1,di代表所有位于ki和ki+1之间的值。对每个虚拟键di,一次搜索对应于di的概率是qi。定义在T内一次搜索的期望代价为E=∑(depth(ki)+1)*pi+∑(depth(di)+1)*qi=1+∑depth(ki)*pi+∑depth(di)*qi


要找的最小的E则为最优二叉查找树

首先,我们定义

w[i][j] = q[i-1]+p[i]+q[i]+p[i+1] + …. +q[j]+p[j],  显然w[i][j] = w[i][j-1] + q[j]+p[j]

E[i][j]为由结点i到j构成的最优二叉查找树的期望代价,如果我们假设它的根为r,那么,显然,他的左子树(由结点i,i+1,…r-1构成)也是一棵最优二叉查找树,右子树也是一棵最优二叉查找树,否则,我们可以调整他的子树,便得到一棵比原先更优的二叉查找树,与前提矛盾,所以,最优二叉查找树具有最优子结构

单单就左子树的查找代价为E[i][r-1]

单单就右子树的查找代价为E[r+1][j]

而将左子树和右子树作为子树连接到一个根r上面,显然,整体的查找代价增加了p[r] +( q[i-1]+p[i]+…q[r-1] )[注:左子树增加的,因为所有的实结点和虚节点均多了一层]+ (q[r+1]+p[r+1]+…q[j])[注:右子树增加的代价]

增加的代价其实转换一下就是w[i][j]

那么,E[i][j] = E[i][r-1] + E[r+1][j] + W[i][j] (r=i, …j)

要使得E[i][j]最小,就要选取合适的r用公式表达即为

E[i][j] = min{E[i][r-1] + E[r+1][j] + W[i][j] }(r=i, …j)


注意E[i][i-1]定义为q[i-1],同理E[j+1][j]为q[j]原因是因为r=i,或j的时候虽然左或右子树虽然为空,但是依旧包括虚节点d[i-1]以及d[j],如果想不通的话可以通过E[i][i]反推E[i][i-1]

反推如下


E[i][i] = E[i][i-1] + E[i+1][i] +  W[i][i] //相当于循环中令r=i

左边 = q[i] + 2*(q[i-1]+q[i]) //定义

右边w[i][i] = q[i-1]+p[i]+q[i]

所以得到E[i][i-1]+E[i+1][i] = q[i-1]+q[i]


有了上面的思路,我们就可以从一个结点出发,慢慢地扩充,每一步的扩充都基于之前更少结点的W和E,这种扩充方式有点类似于矩阵连乘问题的最小乘法次数

#include <iostream>
#include <limits>
#define N 5
using namespace std;


void BST(double q[], double p[], double w[][N+1], double e[][N+1], int root[][N+1]) {
  //将w[i][i-1], e[i][i-1]初始化
  for (int i = 1; i <= N+1; i++) {
    w[i][i-1] = q[i-1]; //保证在求w[i][i]的时候q[i-1]+p[i]+q[i]=w[i][i] = w[i][i-1]+p[i]+q[i]
    e[i][i-1] = q[i-1]; //当r选择i或j作为根的时候,虽然左子树或右子树不包含关键字,但是依然包含虚拟键di-1和dj
    //对应到这边就是e[i][i-1]=q[i-1]以及e[j+1][j] = q[j];可以自己模拟试试就可以很轻松地发现这个规律
  }


  for (int num = 1; num <= N; num++) {// d表示当前循环求几个结点构成的二叉树的最小代价
    for (int i = 1; i <= N-num+1; i++) {
      int j = i+num-1; // 表示当前有num个结点参与


      //搜索最小的e[i][j]
      e[i][j] = INT_MAX;
      w[i][j] = w[i][j-1] + p[j] + q[j];
      for (int r = i; r <= j; r++) {
        double tmp = e[i][r-1] + e[r+1][j] + w[i][j];
        if (tmp < e[i][j]) {
          e[i][j] = tmp;
          root[i][j] = r;
        }
      }


    }
  }
}


int main() {
  double p[N+1] = {0.00,0.15,0.10,0.05,0.10,0.20};
  double q[N+1] = {0.05,0.10,0.05,0.05,0.05,0.10};
  int root[N+1][N+1];
  double w[N+1][N+1];
  double e[N+1][N+1];
  BST(q, p, w, e, root);
  for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= N; j++) {
      cout << root[i][j] << " ";
    }
    cout << endl;
  }
  return 0;
}
    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/ray_seu/article/details/8439890
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞