算法导论读书笔记(19)
最优二叉搜索树
最优二叉搜索树 (optimal binary search tree)问题的形式化定义以下:给定一个由 n 个互异的关键字组成的序列 K = < k1 , k2 , … , kn >,且关键字有序(有 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 。下图是 n = 5个关键字的集合上的两棵二叉查找树。算法
其中各结点的几率以下表所示:3d
每一个关键字 ki 是一个内部结点,每一个虚拟键 di 是一个叶子。每次搜索要么成功(找到某个关键字 ki ),要么失败(找到某个虚拟键 di ),所以有以下公式:code
由于已知了每一个关键字和每一个虚拟键被搜索的几率,于是能够肯定在一棵给定的二叉查找树 T 内一次搜索的指望代价。假设一次搜索的实际代价为检查的结点个数,即在 T 内搜索所发现的结点的深度加1.全部在 T 内一次搜索的指望代价为blog
其中 depthT 表明树 T 内一个结点的深度。递归
对给定的一组几率,咱们的目标是构造一个指望搜索代价最小的二叉查找树。把这种树称为最优二叉查找树。下面将使用动态规划方法来解决这个问题。it
步骤1:一棵最优二叉查找树的结构
为描述一棵最优二叉查找树的最优子结构,首先要看它的子树。一棵二叉查找树的任意一棵子树一定包含在连续范围内的关键字 ki ,…, kj ,有 1 <= i <= j <= n 。另外,一棵含有关键字 ki ,…, kj 的子树一定也含有虚拟键 di-1 ,…, dj 做为叶子。table
如今咱们能够描述最优子结构:若是一棵最优二叉查找树 T 有一棵包含关键字 ki ,…, kj 的子树 T’ ,那么这棵子树 T‘ 对于关键字 ki ,…, kj 和虚拟键 di-1 ,…, dj 的子问题也一定是最优的。class
使用最优子结构来讲明能够根据子问题的最优解,来构造原问题的一个最优解。给定关键字 ki ,…, kj ,假设 kr ( i <= r <= j ),将是包含这些键的一棵最优子树的根。根 kr 的左子树包含关键字 ki ,…, kr-1 (和虚拟键 di-1 ,…, dr-1 ),右子树包含关键字 kr+1 ,…, kj (和虚拟键 dr ,…, dj )。咱们只要检查全部的候选根 kr ,而且肯定全部包含关键字 ki ,…, kr-1 和 kr+1 ,…, kj 的最优二叉查找树,就能够保证找到一棵最优的二叉查找树。读书笔记
步骤2:一个递归解
如今能够递归地定义一个最优解的值了。选取子问题域为找一个包含关键字 ki ,…, kj 的最优二叉查找树,其中 i >= 1 , j <= n 且 j >= i - 1。(当 j >= i - 1时没有真是的关键字,只有虚拟键 di-1 )。定义 e [ i , j ]为搜索一棵包含关键字 ki ,…, kj 的最优二叉查找树的指望代价。最终,咱们要计算的是 e [ 1 , n ]。搜索
当 j = i - 1时出现简单状况。此时只有虚拟键 di-1 。指望的搜索代价为 e [ i , i - 1 ] = qi - 1。
当 j >= i 时,须要从 ki ,…, kj 中选择一个根 kr ,而后用关键字 ki ,…, kr-1 来构造一棵最优二叉查找树做为其左子树,并用关键字 kr+1 ,…, kj 来构造一棵最优二叉查找树做为其右子树。当一棵树成为一个结点的子树时,子树中每一个结点的深度增长1。这个子树的指望搜索代价增长为子树中全部几率的总和。对一棵有关键字 ki ,…, kj 的子树,定义几率的总和为
所以,若是 kr 是一棵包含关键字 ki ,…, kj 的最优子树的根,则有
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 )
能够将 e [ i , j ]重写为
e [ i , j ] = e [ i , r - 1 ] + e [ r + 1 , j ] + w ( i , j )
假设咱们知道该采用哪个结点 kr 做为根。咱们选择有最低指望搜索代价的结点做为根,从而获得最终的递归式:
步骤3:计算一棵最优二叉查找树的指望搜索代价
下面的伪码以几率 p1 ,…, pn 和 q1 ,…, qn 以及规模为 n 为输入,返回表 e 和 root 。
OPTIMAL-BST(p, q, n) 1 let e[1 .. n + 1, 0 .. n], w[1 .. n + 1, 0 .. n] and root[1 .. n, 1 .. n] be new tables 2 for i = 1 to n + 1 3 e[i, i - 1] = q_i - 1 4 w[i, i - 1] = q_i - 1 5 for l = 1 to n 6 for i = 1 to n - l + 1 7 j = i + l - 1 8 e[i, j] = ∞ 9 w[i, j] = w[i, j - 1] + p_j + q_j 10 for r = i to j 11 t = e[i, r - 1] + e[r + 1, j] + w[i, j] 12 if t < e[i, j] 13 e[i, j] = t 14 root[i, j] = r 15 return e and root
此过程的操做比较直观。第1~4行初始化 e [ i , j - 1 ]和 w [ i , j - 1 ]的值。第5~14行的 for
循环利用上面两个递归式来计算 e [ i , j ]和 w [ i , j ],在第10~14行,尝试每一个下标 r 以肯定使用哪一个关键字 kr 来做为包含关键字 ki ,…, kj 的最优二叉查找树的根。不管什么时候发现一个更好的关键字来做为根,这个 for
循环在 root [ i , j ]中保存下标 r 的当前值。
下图是根据上面二叉查找树的关键字分布,程序 OPTIMAL-BST
计算出的表 e [ i , j ]和 w [ i , j ]和 root [ i , j ]。
OPTIMAL-BST
过程须要 Θ ( n3 )。