算法导论 — 15.5 最优二叉搜索树

算法导论 — 15.5 最优二叉搜索树笔记 二叉搜索树满足如下性质 假设 xxx 是二叉搜索树中的一个结点 如果 lll 是 xxx 的左子树的一个结点 那么 l key x keyl key x keyl key x key 如果 rrr 是 xxx 的右子树的一个结点 那么 r key x keyr key x keyr key x key 也就是说 二叉搜索树中的任意一个结点 它的左子树中的所有结点都不大于它 它的右子树中

笔记

二叉搜索树满足如下性质:假设 x x x是二叉搜索树中的一个结点。如果 l l l x x x的左子树的一个结点,那么 l . k e y ≤ x . k e y l.key ≤ x.key l.keyx.key。如果 r r r x x x的右子树的一个结点,那么 r . k e y ≥ x . k e y r.key ≥ x.key r.keyx.key
  
  也就是说,二叉搜索树中的任意一个结点,它的左子树中的所有结点都不大于它,它的右子树中的所有结点都不小于它。下图给出了一个二叉搜索树的例子。
  这里写图片描述
  最优二叉搜索树(Optimal Binary Search Tree)问题描述如下。给定一个 n n n个不同关键字的已排序的序列 K [ 1.. n ] = < k 1 , k 2 , … , k n > K[1..n]=

K[1..n]=<k1,k2,,kn>
(因此 k 1 < k 2 < … < k n k_1 < k_2 < … < k_n k1<k2<<kn),我们希望用这些关键字构造一个二叉搜索树。对每个关键字 k i k_i ki,都有一个概率 p i p_i pi表示其搜索概率。搜索过程中有可能遇到不在 K [ 1.. n ] K[1..n] K[1..n]中的元素,因此我们还有 n + 1 n+1 n+1个元素的“伪关键字”序列 D [ 0.. n ] = < d 0 , d 1 , d 2 , … , d n > D[0..n] =

D[0..n]=<d0,d1,d2,,dn>
,表示搜索过程中可能遇到的所有不在 K [ 1.. n ] K[1..n] K[1..n]中的元素。 d 0 d_0 d0表示所有小于 k 1 k_1 k1的元素; d n d_n dn表示所有大于 k n k_n kn的元素;对 i = 1 , 2 , … , n − 1 i = 1, 2, …, n-1 i=1,2,,n1 d i d_i di表示所有在 k i k_i ki k i + 1 k_{i+1} ki+1之间的元素。对每个伪关键字 d i d_i di,也有一个概率 q i q_i qi表示对应的搜索概率。在二叉搜索树中,伪关键字 d i d_i di必然出现在叶结点上,关键字 k i k_i ki必然出现在非叶结点上。每次搜索要么成功(找到某个关键字 k i k_i ki),要么失败(找到某个伪关键字 d i d_i di)。关键字和伪关键字的概率满足:
              这里写图片描述
  假定一次搜索的代价等于访问的结点数,也就是此次搜索找到的结点在二叉搜索树中的深度再加 1 1 1。给定一棵二叉搜索树 T T T,我们可以确定进行一次搜索的期望代价。
        这里写图片描述
  其中 d e p t h T depth_T depthT表示一个结点在二叉搜索树 T T T中的深度。
  
  对于一组给定的关键字和伪关键字,以及它们对应的概率,我们希望构造一棵期望搜索代价最小的二叉搜索树,这称之为最优二叉搜索树。现在我们用动态规划方法来求解最优二叉搜索树问题。









首先我们描述最优二叉搜索树问题的最优子结构:假设由关键字子序列 K [ i . . j ] = < k i , … , k j > K[i..j] =

K[i..j]=<ki,,kj>
和伪关键字子序列 D [ i − 1.. j ] = < d i − 1 , … , d j > D[i-1..j] =

D[i1..j]=<di1,,dj>
构成的一棵最优二叉搜索树以 k r ( i ≤ r ≤ j ) k_r(i ≤ r ≤ j) kr(irj)为根结点。那么它的左子树由子序列 K [ i . . r − 1 ] K[i..r-1] K[i..r1] D [ i − 1.. r − 1 ] D[i-1..r-1] D[i1..r1]构成,这颗左子树显然也是一棵最优二叉搜索树。同样,它的右子树由子序列 K [ r + 1.. j ] K[r+1..j] K[r+1..j] D [ r . . j ] D[r..j] D[r..j]构成,这颗右子树显然也是一棵最优二叉搜索树。
  
  这里有一个值得注意的细节—空子树。如果包含子序列 K [ i . . j ] K[i..j] K[i..j]的最优二叉搜索树以 k i k_i ki为根结点。根据最优子结构性质,它的左子树包含子序列 K [ i . . i − 1 ] K[i..i-1] K[i..i1],这个子序列不包含任何关键字。但请注意,左子树仍然包含一个伪关键字 d i − 1 d_{i-1} di1。同理,如果选择 k j k_j kj为根结点,那么右子树也不包含任何关键字,而只包含一个伪关键字 d j d_j dj
  
  用 e [ i , j ] e[i, j] e[i,j]表示包含关键字子序列 K [ i . . j ] = < k i , … , k j > K[i..j] =

K[i..j]=<ki,,kj>
的最优二叉搜索树的期望搜索代价。我们最终希望计算出 e [ 1 , n ] e[1, n] e[1,n]
  
  对于 j = i − 1 j = i-1 j=i1的情况,由于子树只包含伪关键字 d i − 1 d_{i-1} di1,所以期望搜索代价为 e [ i , i − 1 ] = q i − 1 e[i, i-1] = q_{i-1} e[i,i1]=qi1
  
  当 j ≥ i j ≥ i ji时,我们要遍历以 k i , k i + 1 , … , k j k_i, k_{i+1}, …, k_j ki,ki+1,,kj作为根结点的情况,然后从中选择期望搜索代价最小的情况作为子问题的最优解。假设选择 k r ( i ≤ r ≤ j ) k_r(i ≤ r ≤ j) kr(irj)作为根结点,那么子序列 K [ i . . r − 1 ] K[i..r-1] K[i..r1]构成的最优二叉搜索树作为左子树,左子树的期望搜索代价为 e [ i , r − 1 ] e[i, r-1] e[i,r1];子序列 K [ r + 1.. j ] K[r+1..j] K[r+1..j]构成的最优二叉搜索树作为右子树,右子树的期望搜索代价为 e [ r + 1 , j ] e[r+1, j] e[r+1,j]
  
  当一棵子树链接到一个根结点上时,子树中所有结点的深度都增加了 1 1 1,那么这棵子树的期望搜索代价的增加值为它的所有结点的概率之和。对于一棵包含子序列 K [ i . . j ] K[i..j] K[i..j]的子树,所有结点的概率之和为
            这里写图片描述
  接上文,若 k r ( i ≤ r ≤ j ) k_r(i ≤ r ≤ j) kr(irj)作为包含关键字子序列 K [ i . . j ] K[i..j] K[i..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]=p_r+(e[i,r-1]+w[i,r-1])+(e[r+1,j]+w[r+1,j]) e[i,j]=pr+(e[i,r1]+w[i,r1])+(e[r+1,j]+w[r+1,j])












由于 w [ i , j ] = w [ i , r − 1 ] + p r + w [ r + 1 , j ] w[i, j] = w[i, r-1] + p_r + w[r+1, j] w[i,j]=w[i,r1]+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,r1]+e[r+1,j]+w[i,j]

我们要遍历以 k i , k i + 1 , … , k j k_i, k_{i+1}, …, k_j ki,ki+1,,kj作为根结点的情况,并选择期望搜索代价最小的情况作为子问题的最优解。于是我们可以得到下面的递归式。
        这里写图片描述
   e [ i , j ] e[i, j] e[i,j]给出了最优二叉搜索树子问题的期望搜索代价。我们还需要记录最优二叉搜索树子问题的根结点,用 r o o t [ i , j ] root[i, j] root[i,j]来记录。
  
  根据上文给出的递归式,我们可以采用自下而上的动态规划方法来求解最优二叉搜索树问题。下面给出了伪代码。
  这里写图片描述
  我们可以看到,该问题的求解过程与15.2节矩阵链乘法问题是很相似的。该算法的时间复杂度也与矩阵链乘法问题一样,都为 Θ ( n 3 ) Θ(n^3) Θ(n3)





习题

15.5-1 设计伪代码 CONSTRUCT-OPTIMAL-BST(root) \text{CONSTRUCT-OPTIMAL-BST(root)} CONSTRUCT-OPTIMAL-BST(root),输入为表 r o o t root root,输出是最优二叉搜索树的结构。例如,对图15-10中的 r o o t root root表,应输出
   k 2 k_2 k2为根
   k 1 k_1 k1 k 2 k_2 k2的左孩子
   d 0 d_0 d0 k 1 k_1 k1的左孩子
   d 1 d_1 d1 k 1 k_1 k1的右孩子
   k 5 k_5 k5 k 2 k_2 k2的右孩子
   k 4 k_4 k4 k 5 k_5 k5的左孩子
   k 3 k_3 k3 k 4 k_4 k4的左孩子
   d 2 d_2 d2 k 3 k_3 k3的左孩子
   d 3 d_3 d3 k 3 k_3 k3的右孩子
   d 4 d_4 d4 k 4 k_4 k4的右孩子
   d 5 d_5 d5 k 5 k_5 k5的右孩子
  与图15-9(b)中的最优二叉搜索树对应。
  
  这里写图片描述
15.5-2 7 7 7个关键字的概率如下所示,求其最优二叉搜索树的结构和代价。














i i i 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7
p i p_i pi 0.04 0.04 0.04 0.06 0.06 0.06 0.08 0.08 0.08 0.02 0.02 0.02 0.10 0.10 0.10 0.12 0.12 0.12 0.14 0.14 0.14
q i q_i qi 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05


  最优二叉搜索树如下所示。期望搜索代价为3.12。
  这里写图片描述
  这里写图片描述
  这里写图片描述
  这里写图片描述
15.5-3 假设 OPTIMAL-BST \text{OPTIMAL-BST} OPTIMAL-BST不维护表 w [ i , j ] w[i, j] w[i,j],而是在第9行利用公式(15.12)直接计算 w [ i , j ] w[i, j] w[i,j],然后在第11行使用此值。如此改动会对渐近时间复杂性有何影响?
  
  在每次迭代中,直接利用公式(15.12)计算 w [ i , j ] w[i, j] w[i,j]需要的时间为 O ( n ) O(n) O(n)。然而, w [ i , j ] w[i, j] w[i,j]的计算并不是在最后一层迭代中,并且计算 w [ i , j ] w[i, j] w[i,j]的渐近时间与最后一层迭代的渐近时间是相同的。所以此改动并不改变 OPTIMAL-BST \text{OPTIMAL-BST} OPTIMAL-BST的渐近时间。
  
15.5-4 Knuth[212]已经证明,对所有 1 ≤ i < j ≤ n 1 ≤ i < j ≤ n 1i<jn,存在最优二叉搜索树,其根满足 r o o t [ i , j − 1 ] ≤ r o o t [ i , j ] ≤ r o o t [ i + 1 , j ] root[i, j-1] ≤ root[i, j] ≤ root[i+1, j] root[i,j1]root[i,j]root[i+1,j]。利用这一特性修改算法 OPTIMAL-BST \text{OPTIMAL-BST} OPTIMAL-BST,使得运行时间减少为 Θ ( n 2 ) Θ(n^2) Θ(n2)
  
  在代码一中,在处理每个子问题 [ i , j ] [i, j] [i,j]时,从 i i i j j j进行迭代(代码一第9行)。根据题目所给的结论,在处理每个子问题 [ i , j ] [i, j] [i,j]时,可以改为从 r o o t [ i , j − 1 ] root[i, j-1] root[i,j1] r o o t [ i + 1 , j ] root[i+1, j] root[i+1,j]进行迭代。下面给出了伪代码。
  这里写图片描述
  现在分析 OPTIMAL-BST-KNUTH \text{OPTIMAL-BST-KNUTH} OPTIMAL-BST-KNUTH的运行时间。
  先考虑子问题规模 l > 1 l > 1 l>1的情况。规模为 l l l的子问题一共有 n − l + 1 n-l+1 nl+1个(参见代码三第6行)。每个规模为 l l l的子问题包含 ( r o o t [ i + 1 , j ] − r o o t [ i , j − 1 ] + 1 ) (root[i+1, j] – root[i, j-1] + 1) (root[i+1,j]root[i,j1]+1)次迭代(其中 j = i + l − 1 j = i+l-1 j=i+l1),故求解所有规模为 l l l的子问题所需的迭代次数为
        这里写图片描述
  故求解所有规模为 l ( l > 1 ) l (l > 1) l(l>1)的问题的迭代次数为 Θ ( n ) Θ(n) Θ(n)。而每次迭代时间为 Θ ( 1 ) Θ(1) Θ(1),故求解所有规模为 l l l的子问题的时间为 Θ ( n ) Θ(n) Θ(n)
  而规模为 l = 1 l = 1 l=1的子问题一共有 n n n个,每个子问题需要 Θ ( 1 ) Θ(1) Θ(1)。故求解所有规模为 l = 1 l = 1 l=1的子问题需要 Θ ( n ) Θ(n) Θ(n)时间。
  子问题的规模 l l l的取值有 n n n种情况,故 OPTIMAL-BST-KNUTH \text{OPTIMAL-BST-KNUTH} OPTIMAL-BST-KNUTH的运行时间为 Θ ( n 2 ) Θ(n^2) Θ(n2)


















版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/222375.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午3:55
下一篇 2026年3月17日 下午3:55


相关推荐

  • STM32delay函数应用与说明[通俗易懂]

    STM32delay函数应用与说明[通俗易懂]STM32delay函数应用应用与说明CortexM4内核编程手册有关时钟系统的内容定时函数的实现delay_init函数delay_us函数对与32中的delay函数有很多中形式可以使用,这里提供一些自己使用遇到过的函数类型。CortexM4内核编程手册有关时钟系统的内容p230SysTicktimer(STK)Theprocessorhasa24-bitsystemtimer,SysTick,thatcountsdownfromthereloadvalu

    2022年6月2日
    71
  • 数仓建模—数仓初识(01)

    数仓建模—数仓初识(01)数据仓库 DataWarehous 一般缩写成 DW DWH 数据仓库是一个面向主题的 SubjectOrien 集成的 Integrate 相对稳定的 Non Volatile 反映历史变化 TimeVariant 的数据集合 用于支持管理决策的数据系统

    2026年3月17日
    2
  • FTP下载工具的使用

    FTP下载工具的使用针对遇到的某些FTP的资源无法下载,或者下载容易中断的问题,FTP下载工具帮你完美的解决这个问题。首先下载FTP工具,目前网上大家都推荐的FlashFXP5.1.0.3829官方中文版。PS:给个链接http://dl.pconline.com.cn/html_2/1/89/id=61&pn=0.html#ad=7366下载完成后直接安装运行就可以,同普通软件一样,给个截图如

    2022年6月13日
    33
  • datadog的数据流转

    datadog的数据流转datadog 是个典型的类 zabbix 的 agent 其主要数据流转如下 数据类型有三种 一个是 metric 一个是 server check 一个是 event 分别存到指标数据库 做服务状态标记和事件报警用 但这里面有些坑 collectd 的数据来源有两个 一个是是 check

    2026年2月4日
    2
  • TensorFlow版本与Python版本对应关系以及TensorFlow包的下载

    TensorFlow版本与Python版本对应关系以及TensorFlow包的下载下载地址:https://www.tensorflow.org/install/pip?lang=python2Anconda下Python2.7版本的TensorFlow的安装condacreate-ntfPython=2.7#创建2.7版本的环境condaactivatetf#激活创建的环境pipinstalltensorflow_gpu-1.12…

    2022年5月27日
    426
  • PEB结构块解析_汉字结构三十二法图

    PEB结构块解析_汉字结构三十二法图peb结构块解析:项目需要获取程序运行的一些状态,目前只能获取寄存器信息,故采用fs寄存器获取peb信息,本文主要探索peb中可以获得的进程信息。windbg信息如下:winxp下,和win7不一样,下面为xp环境dtnt!_peb+0x000InheritedAddressSpace:UChar+0x001ReadImageFileExecOptions:

    2025年8月2日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号