最优二叉查找树

(1)二叉查找树(二分检索树)二叉搜索树

T是一棵二元树,它或者为空,或者其每个结点含有一个可以比较大小的数据元素,且有:

  • T的左子树的所有元素比根结点中的元素小;
  • T的右子树的所有元素比根结点中的元素大;
  • T的左子树和右子树也是二叉搜索树。

(2)最优二叉搜索树

给定一个n个关键字的已排序的序列K=<k 1 ,k 2 ,…,k n >( 不失一般性,设k 1 <k 2 <…<k n ),对每个关键字k i ,都有一个概率p i 表示其搜索频率。根据k i和p i 构建一个二叉搜索树T,每个k i 对应树中的一个结点。若搜索对象x等于某个k i ,则一定可以在T中找到结点k i ;若x<k 1 或 x>k n 或 k i <x<k i+1 (1≤i<n), 则在T中将搜索失败。为此引入外部结点d 0 ,d 1 ,…,d n ,用来表示不在K中的值,称为伪结点。这里每个d i 代表一个区间, d 0 表示所有小于k 1 的值, d n 表示所有大于k n 的值,对于i=1,2,…,n-1,d i 表示所有在k i 和k i+1 之间的值。 同时每个d i 也有一个概率qi 表示搜索对象x恰好落入区间d i 的频率。

(3)例

设有n=5个关键字的集合,每个k i 的概率p i 和d i 的概率q i 如表所示:其中


基于该集合,两棵可能的二叉搜索树如下所示。
(4)二叉搜索树的期望搜索代价代价等于从根结点开始访问结点的数量。

从根结点开始访问结点的数量等于结点在T中的深度+1; 二叉搜索树T的期望代价记depth T (i)为结点i在T中的深度,则T搜索代价的期望为:
E[search cost in T]=∑i=1n(depth⁡T(ki)+1)⋅pi+∑i=0n(depth⁡T(di)+1)⋅qi=1+∑i=1ndepth⁡T(ki)⋅pi+∑i=0ndepth⁡T(di)⋅qi\begin{aligned} \mathrm{E}[\text { search cost in } T] &=\sum_{i=1}^{n}\left(\operatorname{depth}_{T}\left(k_{i}\right)+1\right) \cdot p_{i}+\sum_{i=0}^{n}\left(\operatorname{depth}_{T}\left(d_{i}\right)+1\right) \cdot q_{i} \\ &=1+\sum_{i=1}^{n} \operatorname{depth}_{T}\left(k_{i}\right) \cdot p_{i}+\sum_{i=0}^{n} \operatorname{depth}_{T}\left(d_{i}\right) \cdot q_{i} \end{aligned}E[ search cost in T]​=i=1∑n​(depthT​(ki​)+1)⋅pi​+i=0∑n​(depthT​(di​)+1)⋅qi​=1+i=1∑n​depthT​(ki​)⋅pi​+i=0∑n​depthT​(di​)⋅qi​​

上面图中(a)的期望搜索代价为2.80。(b)的期望搜索代价为2.75。

最优二叉搜索树的定义对给定的概率集合,期望搜索代价最小的二叉搜索树称为最优二叉搜索树

  • (1)最优二叉搜索树的最优子结构:如果一棵 最优二叉搜索树T T有一棵包含关键字k i ,…,k j 的子树T’,则T’必然是包含关键字ki ,…,k j 和伪关键字d i-1 ,…,d j 的子问题的最优解。
  • (2)构造最优二叉搜索树利用最优二叉搜索树的最优子结构性来构造最优二叉搜索树。分析:

对给定的关键字k i ,…,k j ,若其最优二叉搜索树的根结点是k r(i≤r≤j),则k r 的左子树中包含关键字k i ,…,k r-1 及伪关键字d i-1,…,d r-1 ,右子树中将含关键字k i+1 ,…,k j 及伪关键字d r ,…,d j 。策略:检查所有可能的根结点k r (i≤r≤j),如果事先分别求出包含关键字k i ,…,k r-1 和关键字k r+1 ,…,k j 的最优二叉搜索子树,则可保证找到原问题的最优解。

  • (3)计算过程: 求解包含关键字k i ,…,k j 的最优二叉搜索树,其中,i≥1,j≤n 且 j≥i-1。

定义e[i,j]:为包含关键字k i ,…,k j 的最优二叉搜索树的期望搜索代价。e[1,n]为问题的最终解。

当j≥i时,我们需要从k i ,…,k j 中选择一个根结点k r ,其左子树包含关键字k i ,…,k r-1 且是最优二叉搜索子树,其右子树包含关键字k r+1 ,…,k j 且为最优二叉搜索子树。

  • (4)若k r 为包含关键字k i ,…,k j的最优二叉搜索树(树根),则其期望搜索代价与左、右子树的期望搜索代价e[i,r-1]和e[r+1,j]有如下递推关系:
    e[i,j]=pr+(e[i,r−1]+w(i,r−1))+(e[r+1,j]+w(r+1,j))e[i, j]=p_{r}+(e[i, r-1]+w(i, r-1))+(e[r+1, j]+w(r+1, j))e[i,j]=pr​+(e[i,r−1]+w(i,r−1))+(e[r+1,j]+w(r+1,j))
    其中,w(i,j)=w(i,r−1)+pr+w(r+1,j)w(i, j)=w(i, r-1)+p_{r}+w(r+1, j)w(i,j)=w(i,r−1)+pr​+w(r+1,j)
    所以得出e[i,j]=e[i,r−1]+e[r+1,j]+w(i,j)e[i, j]=e[i, r-1]+e[r+1, j]+w(i, j)e[i,j]=e[i,r−1]+e[r+1,j]+w(i,j)

  • (5)有上面的分析,可以得到kr的递推公式

e[i,j]={qi−1if j=i−1min⁡i≤r≤j{e[i,r−1]+e[r+1,j]+w(i,j)}if i≤je[i, j]=\left\{\begin{array}{ll}q_{i-1} & \text { if } j=i-1 \\ \min _{i \leq r \leq j}\{e[i, r-1]+e[r+1, j]+w(i, j)\} & \text { if } i \leq j\end{array}\right.e[i,j]={qi−1​mini≤r≤j​{e[i,r−1]+e[r+1,j]+w(i,j)}​ if j=i−1 if i≤j​

边界条件:j=i-1,存在e[i, i-1]和e[j+1, j]的边界情况。此时,子树不包含实际的关键字,而只包含伪关键字d i-1 ,其期望搜索代价为:e[i,i−1]=qi−1e[i, i-1]=q_{i-1}e[i,i−1]=qi−1​

  • (6)构造最优二叉搜索树

定义root[i,j],保存计算e[i, j]时使e[i, j]取得最小值的r。在求出e[1,n]后,利用root的记录构造出整棵最优二叉搜索树。

  • (7)计算最优二叉搜索树的期望搜索代价
e[1..n+1,0..n]:用于记录所有e[i,j]的值。注:e[n+1,n]对应伪关键字d n 的子树;e[1,0]对应伪关键字d 0 的子树。
root[1..n]:用于记录所有最优二叉搜索子树的根结点,包括整棵最优二叉搜索树的根。
w[1..n+1,0..n]:用于子树保存增加的期望搜索代价,

且有w[i,j]=w[i,j−1]+pj+qjw[i, j]=w[i, j-1]+p_{j}+q_{j}w[i,j]=w[i,j−1]+pj​+qj​

这样,对于Θ(n 2 )个w[i,j],每个的计算时间仅为Θ(1)。

上面算法是构造root表的算法

举例说明

(1)给出一组数据

i012345pi0.150.100.050.100.20qi0.050.100.050.050.050.10\begin{array}{c|cccccc} i & 0 & 1 & 2 & 3 & 4 & 5 \\ \hline p_{i} & & 0.15 & 0.10 & 0.05 & 0.10 & 0.20 \\ q_{i} & 0.05 & 0.10 & 0.05 & 0.05 & 0.05 & 0.10 \end{array}ipi​qi​​00.05​10.150.10​20.100.05​30.050.05​40.100.05​50.200.10​​

(2)根据w[i,j]=w[i,j−1]+pj+qjw[i, j]=w[i, j-1]+p_{j}+q_{j}w[i,j]=w[i,j−1]+pj​+qj​求出w表的内容

(3)根据
e[i,j]={qi−1if j=i−1min⁡i≤r≤j{e[i,r−1]+e[r+1,j]+w(i,j)}if i≤je[i, j]=\left\{\begin{array}{ll}q_{i-1} & \text { if } j=i-1 \\ \min _{i \leq r \leq j}\{e[i, r-1]+e[r+1, j]+w(i, j)\} & \text { if } i \leq j\end{array}\right.e[i,j]={qi−1​mini≤r≤j​{e[i,r−1]+e[r+1,j]+w(i,j)}​ if j=i−1 if i≤j​

求出e表的内容


(4)最后根据上面的算法得出root表的内容

 (5)下面给出C++的最优二叉搜索树的代码实现

  1 #include <iostream>2  3 using namespace std;4  5 const int MaxVal = 9999;6  7 const int n = 5;8 //搜索到根节点和虚拟键的概率9 double p[n + 1] = {-1,0.15,0.1,0.05,0.1,0.2};10 double q[n + 1] = {0.05,0.1,0.05,0.05,0.05,0.1};11  12 int root[n + 1][n + 1];//记录根节点13 double w[n + 2][n + 2];//子树概率总和14 double e[n + 2][n + 2];//子树期望代价15  16 void optimalBST(double *p,double *q,int n)17 {18     //初始化只包括虚拟键的子树19     for (int i = 1;i <= n + 1;++i)20     {21         w[i][i - 1] = q[i - 1];22         e[i][i - 1] = q[i - 1];23     }24  25     //由下到上,由左到右逐步计算26     for (int len = 1;len <= n;++len)27     {28         for (int i = 1;i <= n - len + 1;++i)29         {30             int j = i + len - 1;31             e[i][j] = MaxVal;32             w[i][j] = w[i][j - 1] + p[j] + q[j];33             //求取最小代价的子树的根34             for (int k = i;k <= j;++k)35             {36                 double temp = e[i][k - 1] + e[k + 1][j] + w[i][j];37                 if (temp < e[i][j])38                 {39                     e[i][j] = temp;40                     root[i][j] = k;41                 }42             }43         }44     }45 }46  47 //输出最优二叉查找树所有子树的根48 void printRoot()49 {50     cout << "各子树的根:" << endl;51     for (int i = 1;i <= n;++i)52     {53         for (int j = 1;j <= n;++j)54         {55             cout << root[i][j] << " ";56         }57         cout << endl;58     }59     cout << endl;60 }61  62 //打印最优二叉查找树的结构63 //打印出[i,j]子树,它是根r的左子树和右子树64 void printOptimalBST(int i,int j,int r)65 {66     int rootChild = root[i][j];//子树根节点67     if (rootChild == root[1][n])68     {69         //输出整棵树的根70         cout << "k" << rootChild << "是根" << endl;71         printOptimalBST(i,rootChild - 1,rootChild);72         printOptimalBST(rootChild + 1,j,rootChild);73         return;74     }75  76     if (j < i - 1)77     {78         return;79     }80     else if (j == i - 1)//遇到虚拟键81     {82         if (j < r)83         {84             cout << "d" << j << "是" << "k" << r << "的左孩子" << endl;85         }86         else87             cout << "d" << j << "是" << "k" << r << "的右孩子" << endl;88         return;89     }90     else//遇到内部结点91     {92         if (rootChild < r)93         {94             cout << "k" << rootChild << "是" << "k" << r << "的左孩子" << endl;95         }96         else97             cout << "k" << rootChild << "是" << "k" << r << "的右孩子" << endl;98     }99
100     printOptimalBST(i,rootChild - 1,rootChild);
101     printOptimalBST(rootChild + 1,j,rootChild);
102 }
103
104 int main()
105 {106     optimalBST(p,q,n);
107     printRoot();
108     cout << "最优二叉树结构:" << endl;
109     printOptimalBST(1,n,-1);
110 }

DP实现最优二叉搜索树并打印出树结构

参考自
风沙迷了眼

最优二叉查找树(动态规划)——详解相关推荐

  1. 背包问题动态规划matlab,01背包问题动态规划详解

    计算机算法分析考试:动态规划0-1背包问题,怎么算她说她没醉,却一直摇摇晃晃掉眼泪:你说你爱她,却从未想过给她一个家. 要考试了,老师给划重点有一题:动态规划0-1背包问题,怎么算. 怎么理问题描述: ...

  2. mysql性能调优之 max_allowed_packet 详解 解决ERROR 2006 (HY000): MySQL server has gone away

    mysql性能调优之 max_allowed_packet 详解 一.背景 mysql报错如下: SQLSTATE[HY000]: General error: 2006 MySQL server h ...

  3. python实现连续变量最优分箱详解--CART算法

    今天小编就为大家分享一篇python实现连续变量最优分箱详解–CART算法,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 关于变量分箱主要分为两大类:有监督型和无监督型 对应的分箱方 ...

  4. 最优二叉查找树—动态规划C++

    最优二叉查找树 一.问题描述 二.动态规划算法解题思路 三.解题思路图形化 四.思考:为什么输入概率相同(无序),输出结果不一样 流程图 实例 示例代码 一.问题描述 1.问题描述: 基于统计先验知识 ...

  5. LeetCode1143动态规划详解!

    LeetCode1143 最长公共子序列 1. 问题描述 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列. 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在 ...

  6. leetcode 368. Largest Divisible Subset | 368. 最大整除子集(动态规划详解)

    题目 https://leetcode.com/problems/largest-divisible-subset/ 哎,动态规划对我来说仍然是玄学- 只要有动态规划,medium is harder ...

  7. 矩阵连乘 动态规划 详解

    矩阵连乘问题----动态规划(转载): 给定n个矩阵{A1,A2,-,An},其中Ai与Ai+1是可乘的,i=1,2-,n-1.如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次 ...

  8. 强盗问题动态规划详解

    强盗抢劫一排房间(房间数大于3),每个房间都有钱,不能抢劫两个相邻的房间,要求抢的钱最多.每个房间的钱数用数组表示.数组如:[2,7,9,3,1] 详解: 我们简单分析情况. 1.当只有一间房子的时候 ...

  9. Tomcat8调优核心实战详解

    前言 本文的目的不在于给出最佳配置,而是带领开发者,能够从实际情况出发,通过不断的调节tomcat和jvm参数,去发现吞吐量,平均响应时间和错误率等信息的变化,同时根据服务器的cpu和内存等信息,结合 ...

  10. 超详细!动态规划详解分析(典型例题分析和对比,附源码)

    为啥要使用动态规划?什么时候使用动态规划?以下是我的一些理解和心得,如果你感兴趣,就接着往下看吧. 对您有帮助的话记得给我点个赞哟! 动态规划的基本思想 动态规划(Dynamic Programmin ...

最新文章

  1. 网站防火墙探测工具Wafw00f
  2. templateref html内容,angular之ng-template模板加载
  3. Oralce-清除数据的两种思路
  4. 数据结构与算法--图论最短路径算法应用-词阶求解
  5. 在Android中处理屏幕布局变化
  6. 坦克大战-C语言-详注版
  7. 单链表的回文判断(O(n)时间复杂度和O(1)的空间复杂度)
  8. Pandas基础学习
  9. 读书笔记 | 财务会计理论(第7版 William R.Scott)(上)
  10. eNSP实验vlan及交换机接口类型配置
  11. 通过easyx窗口实现贪吃蛇
  12. python二维表转一维表_【习题】一维表转二维表
  13. 8000字前端性能优化技巧总结!(全面)
  14. 使用font-face艺术字失效
  15. Aptana Studio3汉化方式
  16. 相机选型焦距、距离的计算
  17. SOCKS5实现代理服务器(C++)
  18. PPT卡死了?只需要这几个小技巧,瞬间帮你提速!
  19. 会计学原理计算机实验,会计学原理实验报告.doc
  20. 三维扫描体数据的VTK体绘制程序设计

热门文章

  1. Redis面试 - Redis 主从架构
  2. 安卓加java完成登录_从零学习安卓自动化(java+appium方向):完成登录操作+一个主流程(四)...
  3. java中什么是释放已经持有的锁_java多线程什么时候释放锁
  4. ERROR: Unrecognized command line argument: #39;use#39;
  5. java 课后习题 判断用户输入的数是否为质数
  6. webpack Plugin常用 optimization splitChunks UglifyJsPlugin sourceMap
  7. C#LeetCode刷题之#448-找到所有数组中消失的数字(Find All Numbers Disappeared in an Array)
  8. rundll32的使用和使用c#调用dll
  9. java web快速入门_Web安全快速入门
  10. ico图标下载 ico大全_我们可以做些什么来向ICO投资者保证我们不会用他们的钱消失...