您的位置:首页 > 其它

动态规划之最优二叉搜索树

2017-03-26 14:40 381 查看

目录

最优二叉搜索树简介

举例以及详细分析

代码块

测试结果

最优二叉搜索树简介

1、概念引入

  基于统计先验知识,我们可统计出一个数表(集合)中各元素的查找概率,理解为集合各元素的出现频率。比如中文输入法字库中各词条(单字、词组等)的先验概率,针对用户习惯可以自动调整词频——所谓动态调频、高频先现原则,以减少用户翻查次数。这就是最优二叉查找树问题:查找过程中键值比较次数最少,或者说希望用最少的键值比较次数找到每个关键码(键值)。为解决这样的问题,显然需要对集合的每个元素赋予一个特殊属性——查找概率。这样我们就需要构造一颗最优二叉查找树。

2、最优二叉搜索树

最优二叉查找树:

给定n个互异的关键字组成的序列K=(k1,k2,…,kn),且关键字有序(k1小于k2小于…小于kn),我们想从这些关键字中构造一棵二叉查找树。对每个关键字ki,一次搜索搜索到的概率为pi。可能有一些搜索的值不在K内,因此还有n+1个“虚拟键”d0,d1,…,dn,他们代表不在K内的值。具体:d0代表所有小于k1的值,dn代表所有大于kn的值。而对于i = 1,2,…,n-1,虚拟键di代表所有位于ki和ki+1之间的值。对于每个虚拟键,一次搜索对应于di的概率为qi。要使得查找一个节点的期望代价(代价可以定义为:比如从根节点到目标节点的路径上节点数目)最小,就需要建立一棵最优二叉查找树。

图一显示了给定上面的概率分布pi、qi,生成的两个二叉查找树的例子。图二就是在这种情况下一棵最优二叉查找树。



举例以及详细分析

动态规划法策略是将问题分成多个阶段,逐段推进计算,后继实例解由其直接前趋实例解计算得到。对于最优BST问题,利用减一技术和最优性原则,如果前n-1个节点构成最优BST,加入一个节点an 后要求构成规模n的最优BST。按 n-1, n-2 , … , 2, 1 递归,问题可解。自底向上计算:C[1, 2]→C[1, 3] →… →C[1, n]。为不失一般性用

C[i, j] 表示由{a1,a2,a3……an}构成的BST的耗费。其中1≤i ≤j ≤n。这棵树表示为Tij。从中选择一个键ak作根节点,它的左子树为Tik-1,右子树为Tk+1j。要求选择的k 使得整棵树的平均查找次数C[i, j]最小。左右子树递归执行此过程。(根的生成过程)

递推计算式如下:



基本算法:



代码块

#include<stdio.h>
#include<stdlib.h>
#define max 9999

void DptimalBST(int num, float *p, float **c, int **r)
{
int d, i, j, k, kmin, s;
float temp, sum;
for (i = 1; i < num + 1; i++)
{
c[i][i-1] = 0;//这里指的是在[i,i-1]的区间上没有空间能放k
c[i][i] = p[i];//在[i,i]区间上只有i本身能当k,所以概率是自己本身
r[i][i] = i;
}
c[num + 1][num] = 0;
for (d = 1; d <= num - 1; d++)//d是[i,j]之间的距离
{
for (i = 1; i <= num - d; i++)//i是[i,j]的起始点i
{
j = i + d;//起始点i+距离d=终止点j
temp = max;
for (k = i; k <= j; k++)//在[i,j]之间寻找概率最小的
{
if (c[i][k - 1] + c[k + 1][j] < temp)
{
temp = c[i][k - 1] + c[k + 1][j];
kmin = k;
}
}
r[i][j] = kmin;//记录最优根
sum = p[i];
for (s = i + 1; s <= j; s++)
{
sum += p[s];
}
c[i][j] = temp + sum;
}
}
}

void DptimalBSTPrint(int first, int last, int **r)
{
int k;
if (first <= last)
{
k = r[first][last];
printf("%d ", k);
DptimalBSTPrint(first, k - 1, r);
DptimalBSTPrint(k + 1, last, r);
}
}
int main(void)
{
int i;
int num;
printf("节点的个数:");
scanf("%d", &num);

float *p = (float*)malloc(sizeof(float)*(num + 1));
for (i = 1; i < num+1; i++)
{
printf("节点%d的概率: ", i);
scanf("%f",&p[i]);
}
float **c = (float **)malloc(sizeof(float *)*(num + 2));
for (i = 0; i < num + 2; i++)
{
c[i] = (float *)malloc(sizeof(float)*(num + 1));

}
int **r = (int **)malloc(sizeof(int *)*(num + 2));
for (i = 0; i < num + 2; i++)
{

r[i] = (int *)malloc(sizeof(int)*(num + 1));
}

DptimalBST(num, p, c, r);
printf("该最有二叉搜索树的期望代价为:%f\n", c[1][num]);
printf("构成的最优二叉查找树的中序遍历结果为:");
DptimalBSTPrint(1, num, r);

system("pause");
return 0;
}


测试结果

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