二叉最优搜索树java实现
程序员文章站
2023-12-26 23:59:15
...
package com.bysj.common.算法.动态规划;
/**
* 二叉最优搜索树使用场景:
*
* 现在我有一张英文单词的文章,我要根据字典翻译为中文,所以我先要把字典构造成一个二叉最优搜索树,其中有的字搜索频率很高比如:草,爱,做等
* 所以我先给每个字定义一个频率q,这样每次搜索一个字的时候所需要的消耗就是pi*(depth{ki}+1){算法导论有论证};现在我们要构造一个遍历所有节点的时候消耗最低的
* 二叉树;
*
* 思路:
* 0.(暴力法)每个节点每个位置都要计算一次,所以是2的n次方效率(傻逼)
* 1.首先我们肯定要遍历所有的节点作为根节点的情况
* 2.既然我们要遍历所有节点,但是可以通过动态规划避免求很重复的子树
* 3.动态规划算法的精髓:
* {3.1:构造一个子最优解T,如果T是最优解,那么T1的子树也必定是最优解,证明:T1不是最优解则存在最优解T2,用T2替换T1就会得到更优的T与假设不相符
* 3.2:我们需要从(ki,ki+1,...,kj)中选择一个跟结点kr,然后分别构造其左子树和右子树。下面需要计算以krE为根的树的期望搜索代价。然后选择导致最小期望搜索代价的kr做根。
* 3.2:我们使用自底向上的方法遍历(自顶向下也可以,使用备忘录)
* }
*/
public class 二叉最优搜索树 {
public static void main(String[] args) {
double[] p = {0, 0.15, 0.1, 0.05, 0.1, 0.2}; //n=5关键字有5个
double[] q = {0.05, 0.1, 0.05, 0.05, 0.05, 0.1}; //叶子结点有n+1 = 6个
///这里的关键字长度为5
int n = p.length;
System.out.println("输出根节点辅助表");
int[][] root = Optimal_BST(p, q, n - 1);
int temp = root.length - 1;
for (int i = 1; i < temp; i++) {
for (int j = 1; j < temp; j++) {
System.out.print(root[i][j] + "-");
}
System.out.println();
}
printOptimalBST(root, 1, 5, root[1][5]);
}
/**
* DP在计算最优二叉树的辅助表的算法实现
*
* @param p
* @param q
* @param n
* @return
*/
private static int[][] Optimal_BST(double[] p, double[] q, int n) {
double[][] e = new double[n + 2][n + 2];//
double[][] w = new double[n + 2][n + 2];
int[][] root = new int[n + 2][n + 2];
//初始化叶子结点的值
for (int i = 1; i <= n + 1; i++) {
e[i][i - 1] = q[i - 1];
w[i][i - 1] = q[i - 1];
}
for (int l = 1; l <= n; l++) {///最外层循环是逐渐的将关键字个数从一个扩展到n个
for (int i = 1; i <= n - l + 1; i++) {
int j = i + l - 1;
e[i][j] = Double.MAX_VALUE;
w[i][j] = w[i][j - 1] + p[j] + q[j];//备忘录记录每个次数据
for (int r = i; r <= j; r++) {
double t = e[i][r - 1] + e[r + 1][j] + w[i][j];
if (t < e[i][j]) {
e[i][j] = t;
root[i][j] = r;///存储根节点的位置
}
}
}
}
System.out.println("输出当前的最小代价:" + e[1][n]);
return root;
}
/**
* 构建最优二叉搜索树
*
* @param root
* @param i
* @param j
* @param k
*/
private static void printOptimalBST(int[][] root, int i, int j, int r) {
int rootChild = root[i][j];
if (rootChild == r) {
System.out.println("K" + rootChild + "是根");
printOptimalBST(root, i, rootChild - 1, rootChild);
printOptimalBST(root, rootChild + 1, j, rootChild);
return;
}
if (j < i - 1) {
return;
} else if (j == i - 1)//遇到虚拟键
{
if (j < r) {
System.out.println("d" + j + "是" + "k" + r + "的左孩子");
} else {//j>=r
System.out.println("d" + j + "是" + "k" + r + "的右孩子");
}
return;
} else//遇到内部结点
{
if (rootChild < r) {
System.out.println("k" + rootChild + "是" + "k" + r + "的左孩子");
} else {
System.out.println("k" + rootChild + "是" + "k" + r + "的右孩子");
}
}
printOptimalBST(root, i, rootChild - 1, rootChild);
printOptimalBST(root, rootChild + 1, j, rootChild);
}
}
贴一张算法导论的自下而上的图: