您的位置:首页 > 其它

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

2017-03-15 10:17 477 查看
1.最优二叉查找树

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

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

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

如图


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

此外每次搜索都有成功和不成功两种(分别对应到ki和di)那么有


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 

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]
);
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
c10f
"+j+"是K"+r+"的右孩子");
}
}
printOptimalBST(root,i,rootChild-1,rootChild);
printOptimalBST(root,rootChild+1,j,rootChild);
}
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: