动态规划--4.最优二叉查找树

1.最优二叉查找树

(1)左孩子<根<右孩子

(2)树内关键字k1…kn(中间节点k1<k2<kn)它们对应的搜索概率p1,p2,pn ; 有可能要搜索的关键字不在k中 (如字典树里没有对单词hello的解释)这些点就是虚拟键d(叶子)

d0,d1,dn ;d0表示小于k1的所有值, dn表示大于kn的所有值,它们对应的搜索概率是 q0,q1…qn 

如图《动态规划--4.最优二叉查找树》

(3)每棵树每个节点的期望是 (搜索深度+1)*概率,为什么搜索深度+1不是搜索深度?如果只是搜索深度*概率的话,那么根节点期望岂不是0*概率=0了。

此外每次搜索都有成功和不成功两种(分别对应到ki和di)那么有《动态规划--4.最优二叉查找树》

2.最优子结构

e[i,j] 表示包含 ki…kj关键字的树的搜索代价,设这几个关键字组成的树以 kr 为根(i<=r<=j) 那么对应的左右子树分别是 ki…kr-1  和 kr+1…kj,它们对应的叶子分别为 di-1…dr-1

和 dr…dj。OK树构造完了,分为两种情况

(1)只有一个叶子组成的子树 j=i-1, 只有di-1, 那么e[i,j] = qi-1 这个子树为1个叶节点 不在划分左右子树 如上图a中的d2

(2)子树还包含关键字 即继续划分左右子树 i<=j。那么e[i ,j]= min{ e[i, r-1] + e[r+1,j] + w(i ,j) } 以kr为根的代价中最小值 r从i到j 《动态规划--4.最优二叉查找树》《动态规划--4.最优二叉查找树》

OPTIMAL-BST(p[],q[],n){
let e[1,...n+1,0,...n] and root[1,...n,1,...n] and  w[1,...n+1,0,...n]
//e[1,0]表示只有伪关键字d0的代价,e[n+1,n]表示只有伪关键字dn的代价  
//在w[1,...n+1,0,...n]的下标含义一致

//初始化e[i, i - 1]和 w[i, i - 1]
for i ← 1 to n + 1
   do e[i, i - 1] ← qi-1
      w[i, i - 1] ← qi-1
for l ← 1 to n
   do for i ← 1 to n - l + 1
     do j ← i + l - 1
       e[i, j] ← ∞
       w[i, j] ← w[i, j - 1] + pj + qj
       for r ← i to j
         do t ← e[i, r - 1] + e[r + 1, j] + w[i, j]
           if t < e[i, j]
             then e[i, j] ← t
               root[i, j] ← r
  return e and root
}

3.代码实现  

与矩阵链相乘类似

/**
 * 
 */
package optimal_BST;

/**
 * @author LingLee
 *动态规划 最优二叉查找树 和矩阵链相乘类似 都是三层循环
 */
public class OptimalBST {

	public static void main(String[] args){
		double[] p={0,0.15,0.1,0.05,0.1,0.2};//n=5,5个关键字的搜素概率,p0=0哨兵 概率p1到pn
		double[] q={0.05,0.1,0.05,0.05,0.05,0.1}; //叶子节点有n+1个,概率从q0到qn
		int n=p.length;
		System.out.println("输出根节点的辅助表");
		int[][] root = Optimal_BST(p,q,n-1);
        int temp = root.length-1;
        System.out.println("根:K"+root[1][5]);

        printOptimalBST(root,1,5,root[1][5]);
	}
	
	//计算最优二叉查找树的辅助表
	public static int[][] Optimal_BST(double[] p,double[] q,int n){
		double[][] e=new double[n+2][n+2];//e[i][j]表示包含ki...kj的搜索概率
		double[][] w=new double[n+2][n+2];//w[i][j]表示 ki...kj的概率总和
		int[][] root=new int[n+2][n+2];
		
		//初始化叶子节点的值
		for(int i=1;i<=n+1;i++){//叶子节点一共n+1个是从d0到dn
			e[i][i-1]=q[i-1];
			w[i][i-1]=q[i-1];
		}
		
		for(int l=1;l<=n;l++){//表示从k1到kl有l个关键字参与构造树,1<<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++){//关键字 ki...kj选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("root");
		for(int i=0;i<n+2;i++){
			for(int j=0;j<n+2;j++){
				System.out.print(root[i][j]+"\t");
			}
			System.out.println();
		}
		System.out.println("输出当前的最小代价"+e[1][n]);
		return root;
	}
	
	//构建最优二叉搜索树
	public 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){//只有一个虚拟键di-1
			if(j<r){
				System.out.println("d"+j+"是K"+r+"的左孩子");
			}else{
				System.out.println("d"+j+"是K"+r+"的右孩子");
			}
			return;
		}else{//内部节点 关键字k
			if(rootChild<r){//关键字kr的左孩子
				System.out.println("k"+j+"是K"+r+"的左孩子");
			}else{
				System.out.println("k"+j+"是K"+r+"的右孩子");
			}
		}
		printOptimalBST(root,i,rootChild-1,rootChild);
		printOptimalBST(root,rootChild+1,j,rootChild);
	}
}

《动态规划--4.最优二叉查找树》

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/u012813201/article/details/62215141
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞