最优二叉搜索树

定义

数据集:S=[x1,x2,…,xn]S=[x_1, ~x_2,~\dots~, ~x_n]S=[x1​, x2​, … , xn​]

截图来自:北大公开课 算法设计与分析 最优二叉搜索树算法 ,下同

存取概率分布:P=[a0,b1,a1,b2,…,ai,bi+1,…,bn,an]P=[a_0, ~b_1, ~a_1, ~b_2, ~\dots~, ~a_i, ~b_{i+1}, ~\dots~, ~b_n, ~a_n]P=[a0​, b1​, a1​, b2​, … , ai​, bi+1​, … , bn​, an​]

其中aia_iai​是结点匹配到的概率,bib_ibi​是结点不被匹配到的概率

结点xix_ixi​在T中的深度是d(xi),i=1,2,…,nd(x_i), ~i=1,2,\dots,nd(xi​), i=1,2,…,n

空隙LiL_iLi​的深度为d(Lj),j=0,1,…,nd(L_j), ~j=0,1,\dots,nd(Lj​), j=0,1,…,n

树的深度从0开始计算,根结点的深度为0

对于结点xix_ixi​,其比较次数为深度+1(即 d(xi)+1d(x_i)+1d(xi​)+1),而空隙LiL_iLi​的比较次数恰好为深度的值d(Lj)d(L_j)d(Lj​)

平均比较次数ttt:
t=∑i=1nbi(d(xi)+1)+∑j=0najd(Lj)t=\sum^n_{i=1}b_i(d(x_i)+1)+\sum^n_{j=0}a_jd(L_j) t=i=1∑n​bi​(d(xi​)+1)+j=0∑n​aj​d(Lj​)

构建动态规划

步骤

  1. 子问题边界限定
  2. 如何将该问题归结为更小的子问题
  3. 优化函数的递推方程及初始值
  4. 计算顺序
  5. 是否需要标记函数
  6. 时间复杂度

子问题划分

子问题边界:(i,j)(i,j)(i,j)

数据集:S[i,j]=[xi,xi+1,…,xj]S[i,j]=[x_i,~x_{i+1},~\dots~,~x_j]S[i,j]=[xi​, xi+1​, … , xj​]

存储概率分布:P[i,j]=[ai−1,bi,ai,bi+1,…,bj,aj]P[i,j]=[a_{i-1},\ b_i,\ a_i,\ b_{i+1},\ \dots,\ b_j,\ a_j]P[i,j]=[ai−1​, bi​, ai​, bi+1​, …, bj​, aj​]

对一棵树进行划分,假设以xix_ixi​作为根结点,则其左右子树是两个子问题,子问题的划分分别为:

  1. 左子树:S[i,k−1],P[i,k−1]S[i,\ k-1],\ P[i,\ k-1]S[i, k−1], P[i, k−1]
  2. 右子树:S[k+1,j],P[k+1,j]S[k+1,\ j],\ P[k+1,\ j]S[k+1, j], P[k+1, j]

举例:

子问题的概率之和
子问题界定为S[i,j]S[i,j]S[i,j]和P[i,j]P[i,j]P[i,j],令w[ij]=∑p=i−1jap+∑q=ljbqw[i\ j]=\displaystyle\sum^j_{p=i-1}a_p + \sum^j_{q=l} b_qw[i j]=p=i−1∑j​ap​+q=l∑j​bq​为P[i,j]P[i,j]P[i,j]中所有概率(数据和空隙)之和

优化函数的递推方程

设m[i,j]m[i,j]m[i,j]是相对于输入S[i,j]S[i,j]S[i,j]和P[i,j]P[i,j]P[i,j]的最优二叉搜索树的最少平均比较次数

递推方程:
m[i,j]=min⁡a≤k≤j{m[i,k−1]+m[k+1,j]+w[i,j]}m[i,\ j] = \min \limits_{a\le k \le j}\{ m[i,\ k-1] + m[k+1,\ j] + w[i,j] \} m[i, j]=a≤k≤jmin​{m[i, k−1]+m[k+1, j]+w[i,j]}
即:左边子问题的最优比较次数,加上右边子问题的最优比较次数,再加上iii到jjj的所有概率的总和w[i,j]w[i,j]w[i,j]

为什么要加上w[i,j]w[i, j]w[i,j]?因为在对左右子问题进行求解后,并上新的根结点xkx_kxk​时,左右子树的结点的深度都会增加1(如右图)。而此时,对子树的每个结点的影响,就是每个子树的比较次数都会增加1,那么就相当于概率多乘了一次,所以后面都加上了对应的www。而每个结点的概率都多加了www,再加上新增的根节点的概率bkb_kbk​,那么,合起来就相当于加了一个w[i,j]w[i, j]w[i,j]

具体原因和公式推导可见下方

方程分解:

设m[i,j]km[i,j]_km[i,j]k​是以第kkk个结点为根的相对于输入S[i,j]S[i,j]S[i,j]和P[i,j]P[i,j]P[i,j]的平均比较次数
m[i,j]k=m[i,k−1]+m[k+1,j]+w[i,j]m[i,\ j]_k = m[i,\ k-1] + m[k+1,\ j] + w[i,j] m[i, j]k​=m[i, k−1]+m[k+1, j]+w[i,j]
总的最优平均比较次数为:
m[i,j]=min⁡{m[i,j]k∣i≤k≤j}m[i,\ j]=\min\{ m[i,\ j]_k \ | \ i \le k \le j\} m[i, j]=min{m[i, j]k​ ∣ i≤k≤j}

也就是说,要求总的最优平均比较次数,需要将这一层的每一个结点都拿去当根结点试一试

递推方程公式证明:

假设令第kkk个结点为根结点,其平均比较次数为:

m[i,j]k=(m[i,k−1]+w[i,k−1])+(m[k+1,j]+w[k+1,j])+1×bk=(m[i,k−1]+m[k+1,j])+(w[i,k−1]+bk+w[k+1,j])=m[i,k−1]+m[k+1,j]+w[i,j]\begin{aligned} m[i,\ j]_k &= (m[i,\ k-1] + w[i,\ k-1]) + (m[k+1,\ j] + w[k+1,\ j]) + 1\times b_k \\ &= (m[i,\ k-1] + m[k+1,\ j]) + (w[i,\ k-1]+b_k+w[k+1,\ j]) \\ &= m[i,\ k-1]+m[k+1,\ j] + w[i,\ j] \end{aligned} m[i, j]k​​=(m[i, k−1]+w[i, k−1])+(m[k+1, j]+w[k+1, j])+1×bk​=(m[i, k−1]+m[k+1, j])+(w[i, k−1]+bk​+w[k+1, j])=m[i, k−1]+m[k+1, j]+w[i, j]​

注意第一行中,(m[i,k−1]+w[i,k−1])+(m[k+1,j]+w[k+1,j])(m[i, k-1] + w[i, k-1]) + (m[k+1, j] + w[k+1, j])(m[i,k−1]+w[i,k−1])+(m[k+1,j]+w[k+1,j])都加了www,这是由于左右子树的深度都增加了1带来的影响。而最后的1×bk1\times b_k1×bk​就代表新的根结点,乘1是比较次数为1

初始值:m[i,i−1]=0m[i,\ i-1]=0m[i, i−1]=0对应子问题为空的情况。

如左子树为空时,对应于S[1,0]S[1,0]S[1,0],m[1,0]=0m[1,0]=0m[1,0]=0的情况

m[i,j]m[i,\ j]m[i, j]中,i,ji,ji,j代表一个结点的区间,而在递推公式中,m[i,j]k=m[i,k−1]+m[k+1,j]+w[i,j]m[i,\ j]_k = m[i,\ k-1] + m[k+1,\ j] + w[i,j]m[i, j]k​=m[i, k−1]+m[k+1, j]+w[i,j],

公式计算实例:北大公开课 算法设计与分析 最优二叉搜索树算法 19:00左右

w的递推公式

w[i][j]=∑p=ija[p]+∑q=ijb[q]w[i][j] = \sum^j_{p=i}a[p] + \sum^j_{q=i}b[q] w[i][j]=p=i∑j​a[p]+q=i∑j​b[q]

用于编程时,可采用:
w[i][j]={a[j],i=j+1w[i][j−1]+b[j]+a[j],i≤jw[i][j] = \begin{cases} a[j], & i=j+1 \\ w[i][j-1] + b[j] + a[j], & i \le j \end{cases} w[i][j]={a[j],w[i][j−1]+b[j]+a[j],​i=j+1i≤j​
其中,当i=ji=ji=j时,w[i][j−1]=0w[i][j-1]=0w[i][j−1]=0,因此有w[i][j]=a[j]+b[j]w[i][j] = a[j]+b[j]w[i][j]=a[j]+b[j]

时间复杂度估计

i,ji,ji,j的所有组合有O(n2)O(n^2)O(n2)种,每种要对不同的kkk进行计算,k=O(n)k=O(n)k=O(n),每次计算为常数时间

时间复杂度:T(n)=O(n3)T(n)=O(n^3)T(n)=O(n3)

空间复杂度:S(n)=O(n2)S(n)=O(n^2)S(n)=O(n2),为mmm数组的大小

代码实现

/*** @author 寒洲* @description 参考视频:https://www.bilibili.com/video/BV1Ls411W7PB?p=46* @date 2021-05-20*/
public class OptimalBinarySearchTree {/*** @param a 结点不被匹配的概率,下标为 [0, n),n为数据量(结点数)* @param b 结点被匹配的概率,下标为 [1, n]* @param m i到j的最优平均检索次数,也就是平均最优搜索值* @param s 保存i到j的最优根节点,就是确定i到j把哪一个结点作为根结点最优* @param w 区间[i, j]之间所有结点和空隙的命中概率。公式为:w[i][j] = a[i-1]+b[i]+...+b[j]+a[j]*/public static void optimalBinarySearchTree(float[] a, float[] b, float[][] m, int[][] s, float[][] w) {// 数据个数,即结点个数int n = a.length - 1;// 赋初始值for (int i = 0; i <= n; i++) {/*这个初始值之后会被用到。这里只有a[i],是因为大多数情况下a都会比b多一个元素,比如w[i][j] = a[i-1] + b[i] + a[i] + ... + b[j] + a[j],可以看到除了第一个a[i-1],其余都可以在一个for循环中相加得到此外,我们也可以发现w[i][j]对应a[i-1],所以下方,下标也是错开的*/w[i + 1][i] = a[i];m[i + 1][i] = 0;}// r表示[i,j]区间的长度for (int r = 0; r < n; r++) {/*下方的for循环中,r表示[i,j]区间的长度,i,j关系对应如下:[1,1] [2,2] ... [n,n][1,2] [2,3] ... [n-1,n][1,3] [2,4] ... [n-2,n]...*/for (int i = 1; i <= n - r; i++) {int j = i + r;/*w的递推公式为:w[i][j] = a[i-1]+b[i]+...+b[j]+a[j] = w[i][j-1] + b[j] + a[j]当r=0时,j=i,则w[i][i-1]的值为0,其初始值已经在上方给出所以当r=0时,w[i][j]=w[i][i]=a[i-1]+b[i]+a[i]当r=1时,w[i][j] = w[i][i+1] = w[i][i] + a[i+1] + b[i+1]= a[i-1] + b[i] + a[i] +  b[i+1] + a[i+1]也就是在w[i][i]的基础上加上新增的a和b,这样子就把[i,i+1]这个区间的概率都加进来了*/w[i][j] = w[i][j - 1] + a[j] + b[j];// 这里先计算出一个值来,后续可以用于找出最小值。不然的话,数组元素默认值为0,不好比较m[i][j] = m[i][i - 1] + m[i + 1][j];s[i][j] = i;// 上面的操作是针对[i][j]的,下面的操作都是发送在[i, j]区间内的// k表示令第k个结点作为根节点。// 在for循环中,要求以k为根节点的最优比较次数for (int k = i + 1; k <= j; k++) {/*m[i][j] = min{ m[i][k-1] + m[k+1][j] + w[i][j] } (i <= k <= j)每次都要+w[i][j]是因为:因为在对左右子问题进行求解后,并上新的根结点x_k时,左右子树的结点的深度都会增加1。而此时,对子树的每个结点的影响,就是每个子树的比较次数都会增加1,那么就相当于概率加了一次。而每个结点的概率都加1,再加上新增的根节点的概率,合起来就相当于加了一个w[i][j]上式又可推导出m[i][j] = w[i][j] + min(m[i][k-1] + m[k+1][j])  (1 <= k <= j)*/// 下方先求出min(m[i][k-1] + m[k+1][j]),得到一个min值float min = m[i][k - 1] + m[k + 1][j];if (min < m[i][j]) {m[i][j] = min;// 保存最优的根节点方案s[i][j] = k;}// min值再加上w,得到m[i][j]m[i][j] += w[i][j];}}}}public static void optimalBinarySearchTree(float[] a, float[] b) {if (a.length == b.length + 1) {float[] b2 = new float[b.length + 1];System.arraycopy(b, 0, b2, 1, b.length);b = b2;}int n = b.length;float[][] m = new float[n + 1][n + 1];int[][] s = new int[n + 1][n + 1];float[][] w = new float[n + 1][n + 1];optimalBinarySearchTree(a, b, m, s, w);display(m);display(s);display(w);}public static void main(String[] args) {float[] b = {0.1F, 0.3F, 0.1F, 0.2F, 0.1F};float[] a = {0.04F, 0.02F, 0.02F, 0.05F, 0.06F, 0.01F};optimalBinarySearchTree(a, b);}public static <T> void display(float[][] arr) {for (float[] ts : arr) {for (float t : ts) {System.out.print(t + "\t");}System.out.println();}System.out.println();}public static <T> void display(int[][] arr) {for (int[] ts : arr) {for (int t : ts) {System.out.print(t + " ");}System.out.println();}System.out.println();}
}

最优二叉搜索树算法 java实现相关推荐

  1. Python:实现最优二叉搜索树算法(附完整源码)

    Python:实现最优二叉搜索树算法 import sys from random import randintclass Node:def __init__(self, key, freq):sel ...

  2. Java实现 二叉搜索树算法(BST)

    一.树 & 二叉树 树是由节点和边构成,储存元素的集合.节点分根节点.父节点和子节点的概念. 如图:树深=4; 5是根节点:同样8与3的关系是父子节点关系. 二叉树binary tree,则加 ...

  3. 二叉搜索树算法演示视频

    二叉搜索树算法演示视频

  4. java二叉查找算法_Java手写二叉搜索树算法

    package com.zyizou.basis.demo.tree; /** * 手写二叉搜索树的逻辑 * * @author zibin */ public class BinarySearchT ...

  5. bst java_图解:二叉搜索树算法(BST)

    一.树 & 二叉树 树是由节点和边构成,储存元素的集合.节点分根节点.父节点和子节点的概念. 如图:树深=4; 5是根节点:同样8与3的关系是父子节点关系. 二叉树binary tree,则加 ...

  6. 算法:最优二叉搜索树

    算法设计第五次作业part2 1.纸面题:对最优二叉树和矩阵连乘两种算法验证四边形法则,如果符合四边形法则则举几个正例,如果不符合则举几个反例 四边形法则 i<i'j<j'w(i,j)+w ...

  7. 最优二叉搜索树(Optimal BST)-算法导论

    问题描述: 维基百科定义: https://en.wikipedia.org/wiki/Optimal_binary_search_tree In the static optimality prob ...

  8. java 递归从子节点删除父节点_LeetCode450. 删除二叉搜索树中的节点

    删除一个二叉搜索树中的节点,需要进行情况的分类讨论,看一下将这个节点删除之后是否需要对二叉搜索树进行调整(为了保持树的连接和维持二叉搜索树的性质). (1)如果删除的是一个叶子节点,那问题不大,因为它 ...

  9. 代码随想录算法训练营第22天 二叉树 java :235. 二叉树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

    文章目录 LeetCode 236. 二叉树的最近公共祖先 题目讲解 思路 LeetCode 701.二叉搜索树中的插入操作 题目讲解 思路 LeetCode 450.删除二叉搜索树中的节点 题目讲解 ...

最新文章

  1. 历届华人 AAAI Fellows
  2. spring-cloud-sleuth+zipkin追踪服务实现(一)
  3. virtualBox中的ubuntu共享文件夹
  4. 大华webplugin控件无法安装_大华监控平台SmartPSS如何上电视墙,一文包你学会
  5. 使用LayoutParams设置布局
  6. 如何测试web服务器性能,如何执行Web服务器性能基准测试?
  7. Pickle Finance:BAC-DAI Pickle Jar将在迁移到BAS v2后更新
  8. VB判断文件及目录的存在性
  9. undo log、rollback segment
  10. C语言 谭浩强第五版 课后习题解答
  11. Richard Hamming:You and your research
  12. 华为数通(一):如何使用ssh console telnet来连接设备
  13. 完整的元器件选型指南
  14. 推荐一个宝藏公众号,附大数据PPT合集下载
  15. 单位转换 inch mm mil
  16. 心形函数的几种表达式
  17. 台湾java程序员工资水平_女程序员在台湾的工资比大陆低吗?
  18. 人类的精神寄托和生命的终极关怀——宗教
  19. gt、lt、ge、le、eq、ne的含义
  20. Dell T40和Dell T140有啥区别?

热门文章

  1. 广州移动烽火HG680-LC_S905L3_安卓9.0_线刷固件包
  2. 浏览器主页被360劫持的解决办法
  3. Day_52_Java高级
  4. 2017年版本 行政区域代码 SQL 语句 【INSERT】
  5. ASM _asm_hbeatiowait
  6. STM32F429操作两片AD7689
  7. 外汇交易系统-Dolly系统系列二 背景、目的和常见问题
  8. ubuntu16.04下安装GTX1080TI显卡驱动+安装CUDA
  9. QT编写实现图片的幻灯片播放、自适应显示、缩放(以鼠标位置为中心进行缩放)、拖动、重置、显示鼠标位置像素坐标及RGB值、播放GIF动画、截图保存、批量保存、拖入文件夹遍历所有文件
  10. tf.saved_model.save模型导出、TensorFlow Serving模型部署、TensorBoard中的HParams 超参数调优