最优二叉查找树(动态规划)

合集下载

最优二叉搜索树

最优二叉搜索树

#include<stdio.h>#include<stdlib.h>#define max 9999void OptimalBST(int,float*,float**,int**);void OptimalBSTPrint(int,int,int**);void main(){int i,num;FILE *point;//所有数据均从2.txt中获取,2.txt中第一个数据表示节点个数;从第二个数据开始表示各个节点的概率point=fopen("2.txt","r");if(point==NULL){printf("cannot open 2.txt.\n");exit(-1);}fscanf(point,"%d",&num);printf("%d\n",num);float *p=(float*)malloc(sizeof(float)*(num+1));for(i=1;i<num+1;i++)fscanf(point,"%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));//动态规划实现最优二叉查找树的期望代价求解。

OptimalBST(num,p,c,r);printf("该最优二叉查找树的期望代价为:%f \n",c[1][num]);//给出最优二叉查找树的中序遍历结果;printf("构造成的最优二叉查找树的中序遍历结果为:");OptimalBSTPrint(1,4,r);}void OptimalBST(int num,float*p,float**c,int**r){int d,i,j,k,s,kmin;float temp,sum;for(i=1;i<num+1;i++)//主表和根表元素的初始化{c[i][i-1]=0;c[i][i]=p[i];r[i][i]=i;}c[num+1][num]=0;for(d=1;d<=num-1;d++)//加入节点序列{for(i=1;i<=num-d;i++){j=i+d;temp=max;for(k=i;k<=j;k++)//找最优根{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;}}}//采用递归方式实现最优根的输出,最优根都是保存在r[i][j]中的。

二叉查找树

二叉查找树

二叉查找树(BST,Binary Search Tree),又名二叉搜索树或二叉检索树,是一颗满足如下条件的树:1、每个节点包含一个键值2、每个节点有最多两个孩子3、对于任意两个节点x和y,它们满足下述搜索性质:a、如果y在x的左子树里,则key[y] <= key[x]b、如果y在x的右子树里,则key[y] >= key[x]最优二叉查找树(Optimal BST,Optimal Binary Search Tree)最优二叉查找树是使查找各节点平均代价最低的二叉查找树。

具体来说就是:给定键值序列K = <k1, k2, . . . , kn>,k1 < k2 <.. <kn,其中键值ki,被查找的概率为pi,要求以这些键值构建一颗二叉查找树T,使得查找的期望代价最低(查找代价为检查的节点数)。

下面是对于查找期望代价的解释:对于键值ki, 如果其在构造的二叉查找树里的深度(离开树根的分支数)为depthT(ki),则搜索该键值的代价= depthT(ki) +1(需要加上深度为0的树根节点)。

由于每个键值被查找的概率分别为pi,i=1,2,3…,n。

所以查找期望代价为:E[T的查找代价] = ∑i=1~n(depthT(ki) +1)*pi时间复杂度1、穷举穷举构造最优二叉查找树,其实就是这样的一个问题:给一个拥有n个数的已排序的节点,可以将其构造成多少种不同的BST(用来找到一个最优的二叉查找树)?设可以构造成T(n)个,那么枚举每一个元素作为根节点的情况,当第一个元素作为根节点时,其余n-1个构成右子树,无左子树,是n-1情况时的子问题,共T(n-1)种;当第二个元素作为根节点时,左子树有1个元素,右子树有n-2个元素,根据乘法原理共有T(1)T(n-2)种情况……依此类推得到:T(n)= (0)T(n-1)+T(1)T(n-2)+T(2)T(n-3)+ ......+T(n-2)T(1)+T(n-1)T(0);此外,有T(0)=T(1)=1。

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

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

动态规划-最优⼆叉搜索树摘要: 本章介绍了⼆叉查找树的概念及操作。

主要内容包括⼆叉查找树的性质,如何在⼆叉查找树中查找最⼤值、最⼩值和给定的值,如何找出某⼀个元素的前驱和后继,如何在⼆叉查找树中进⾏插⼊和删除操作。

在⼆叉查找树上执⾏这些基本操作的时间与树的⾼度成正⽐,⼀棵随机构造的⼆叉查找树的期望⾼度为O(lgn),从⽽基本动态集合的操作平均时间为θ(lgn)。

1、⼆叉查找树 ⼆叉查找树是按照⼆叉树结构来组织的,因此可以⽤⼆叉链表结构表⽰。

⼆叉查找树中的关键字的存储⽅式满⾜的特征是:设x为⼆叉查找树中的⼀个结点。

如果y是x的左⼦树中的⼀个结点,则key[y]≤key[x]。

如果y是x的右⼦树中的⼀个结点,则key[x]≤key[y]。

根据⼆叉查找树的特征可知,采⽤中根遍历⼀棵⼆叉查找树,可以得到树中关键字有⼩到⼤的序列。

介绍了⼆叉树概念及其遍历。

⼀棵⼆叉树查找及其中根遍历结果如下图所⽰:书中给出了⼀个定理:如果x是⼀棵包含n个结点的⼦树的根,则其中根遍历运⾏时间为θ(n)。

问题:⼆叉查找树性质与最⼩堆之间有什么区别?能否利⽤最⼩堆的性质在O(n)时间内,按序输出含有n个结点的树中的所有关键字?2、查询⼆叉查找树 ⼆叉查找树中最常见的操作是查找树中的某个关键字,除了基本的查询,还⽀持最⼤值、最⼩值、前驱和后继查询操作,书中就每种查询进⾏了详细的讲解。

(1)查找SEARCH 在⼆叉查找树中查找⼀个给定的关键字k的过程与⼆分查找很类似,根据⼆叉查找树在的关键字存放的特征,很容易得出查找过程:⾸先是关键字k与树根的关键字进⾏⽐较,如果k⼤⽐根的关键字⼤,则在根的右⼦树中查找,否则在根的左⼦树中查找,重复此过程,直到找到与遇到空结点为⽌。

例如下图所⽰的查找关键字13的过程:(查找过程每次在左右⼦树中做出选择,减少⼀半的⼯作量)书中给出了查找过程的递归和⾮递归形式的伪代码:1 TREE_SEARCH(x,k)2 if x=NULL or k=key[x]3 then return x4 if(k<key[x])5 then return TREE_SEARCH(left[x],k)6 else7 then return TREE_SEARCH(right[x],k)1 ITERATIVE_TREE_SEARCH(x,k)2 while x!=NULL and k!=key[x]3 do if k<key[x]4 then x=left[x]5 else6 then x=right[x]7 return x(2)查找最⼤关键字和最⼩关键字 根据⼆叉查找树的特征,很容易查找出最⼤和最⼩关键字。

最优二叉搜索树问题经典练习题分类汇编

最优二叉搜索树问题经典练习题分类汇编

最优二叉搜索树问题经典练习题分类汇编
问题概述
最优二叉搜索树问题是一种经典的算法问题,涉及确定一个有序数列中的某些元素构成的二叉搜索树,使得其查找效率最高。

本文档旨在为您提供一些经典的练题分类汇编,帮助您更好地理解和解决最优二叉搜索树问题。

分类汇编
基础题目
1.问题描述:给定一组有序数列和每个元素的查找成功概率,求构建一棵二叉搜索树的最均查找时间。

2.相关算法:动态规划算法、递归算法。

进阶题目
1.问题描述:给定一组连续有序的数列和每个元素的权重,求构建一棵二叉搜索树的最均查找时间。

2.相关算法:动态规划算法、递归算法。

其他应用题目
1.问题描述:给定一组非有序的元素和其出现的频率,求构建一棵二叉搜索树的最均查找时间。

2.相关算法:动态规划算法、递归算法。

结论
最优二叉搜索树问题是一个重要且常见的算法问题,通过运用动态规划和递归算法,我们可以求解最优二叉搜索树的构建。

本文档提供了一些经典的练题分类汇编,希望能帮助您更好地掌握这个问题,并应用于实际场景中。

『算法设计_伪代码』动态规划问题

『算法设计_伪代码』动态规划问题

『算法设计_伪代码』动态规划问题这⼀部分伪代码太长,所以只讲解解题⼿段
核⼼思想是将复杂问题化解为两个简单⼀点的问题,递归处理。

零、⼏个概念
最优⼦结构
⼀个问题的最优解包含其⼦问题的最优解
证明:反证法,a=b+c中a的最优解如果不是b和c的最优解,则b和c的最优解和将优于a的最优解,⽭盾,的证。

重叠⼦问题
解决问题的递归算法中会重复求解相同的⼦问题
解法:对每个⼦问题的第⼀次求解存⼊表中,再次求解时直接查询即可。

⼀、割绳⼦问题
r:表⽰最⼤价值
s:表⽰此时分割位置,如i=8时的2表⽰绳⼦分为2、6两段,2、6对应i=2和i=6的r
⼆、矩阵链乘问题
右表记录k值
pi表⽰Ai的第⼆维,pi-1表⽰Ai的第⼀维
三、最长公共⼦序列 (LCS)
问题描述
并不是通常意义上的公共⼦序列(和常见的算法题中的不⼀样),定义如下,
解法⽰意
1. xi=yi时直接将当前的左上格⼦数+1作为本格,箭头指向左上;
2. xi!=yi时⽐较上⽅格⼦和左⾯格⼦:
1. 上⽅不⼩于右侧,箭头指上,copy上⽅格⼦
2. 否则箭头指右,copy右侧格⼦
格⼦图⽣成先补0,然后由上到⼩⼀⾏⼀⾏的填充,⽽结果读取是从右下到左上的⽅向:
四、最优⼆叉查找树问题描述
解法⽰意
W为概率矩阵,e为消耗期望矩阵
先填W,后填e,注意表格⾏1:6,列0:5,root记录对应e位置的r的值。

重建时看root,[1,5]位置为2,表⽰2为根,分解为k1,{k3,k4,k5},再看[3,5]为5,右⼦树5为根……。

算法设计与分析复习题目及答案 (3)

算法设计与分析复习题目及答案 (3)

分治法1、二分搜索算法是利用(分治策略)实现的算法。

9. 实现循环赛日程表利用的算法是(分治策略)27、Strassen矩阵乘法是利用(分治策略)实现的算法。

34.实现合并排序利用的算法是(分治策略)。

实现大整数的乘法是利用的算法(分治策略)。

17.实现棋盘覆盖算法利用的算法是(分治法)。

29、使用分治法求解不需要满足的条件是(子问题必须是一样的)。

不可以使用分治法求解的是(0/1背包问题)。

动态规划下列不是动态规划算法基本步骤的是(构造最优解)下列是动态规划算法基本要素的是(子问题重叠性质)。

下列算法中通常以自底向上的方式求解最优解的是(动态规划法)备忘录方法是那种算法的变形。

(动态规划法)最长公共子序列算法利用的算法是(动态规划法)。

矩阵连乘问题的算法可由(动态规划算法B)设计实现。

实现最大子段和利用的算法是(动态规划法)。

贪心算法能解决的问题:单源最短路径问题,最小花费生成树问题,背包问题,活动安排问题,不能解决的问题:N皇后问题,0/1背包问题是贪心算法的基本要素的是(贪心选择性质和最优子结构性质)。

回溯法回溯法解旅行售货员问题时的解空间树是(排列树)。

剪枝函数是回溯法中为避免无效搜索采取的策略回溯法的效率不依赖于下列哪些因素(确定解空间的时间)分支限界法最大效益优先是(分支界限法)的一搜索方式。

分支限界法解最大团问题时,活结点表的组织形式是(最大堆)。

分支限界法解旅行售货员问题时,活结点表的组织形式是(最小堆)优先队列式分支限界法选取扩展结点的原则是(结点的优先级)在对问题的解空间树进行搜索的方法中,一个活结点最多有一次机会成为活结点的是( 分支限界法).从活结点表中选择下一个扩展结点的不同方式将导致不同的分支限界法,以下除( 栈式分支限界法)之外都是最常见的方式.(1)队列式(FIFO)分支限界法:按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。

(2)优先队列式分支限界法:按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。

《计算机算法设计与分析》课程设计

《计算机算法设计与分析》课程设计

《计算机算法设计与分析》课程设计用分治法解决快速排序问题及用动态规划法解决最优二叉搜索树问题及用回溯法解决图的着色问题一、课程设计目的:《计算机算法设计与分析》这门课程是一门实践性非常强的课程,要求我们能够将所学的算法应用到实际中,灵活解决实际问题。

通过这次课程设计,能够培养我们独立思考、综合分析与动手的能力,并能加深对课堂所学理论和概念的理解,可以训练我们算法设计的思维和培养算法的分析能力。

二、课程设计内容:1、分治法:(2)快速排序;2、动态规划:(4)最优二叉搜索树;3、回溯法:(2)图的着色。

三、概要设计:分治法—快速排序:分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。

递归地解这些子问题,然后将各个子问题的解合并得到原问题的解。

分治法的条件:(1) 该问题的规模缩小到一定的程度就可以容易地解决;(2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;(3) 利用该问题分解出的子问题的解可以合并为该问题的解;(4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

抽象的讲,分治法有两个重要步骤:(1)将问题拆开;(2)将答案合并;动态规划—最优二叉搜索树:动态规划的基本思想是将问题分解为若干个小问题,解子问题,然后从子问题得到原问题的解。

设计动态规划法的步骤:(1)找出最优解的性质,并刻画其结构特征;(2)递归地定义最优值(写出动态规划方程);(3)以自底向上的方式计算出最优值;(4)根据计算最优值时得到的信息,构造一个最优解。

●回溯法—图的着色回溯法的基本思想是确定了解空间的组织结构后,回溯法就是从开始节点(根结点)出发,以深度优先的方式搜索整个解空间。

这个开始节点就成为一个活结点,同时也成为当前的扩展结点。

在当前的扩展结点处,搜索向纵深方向移至一个新结点。

这个新结点就成为一个新的或节点,并成为当前扩展结点。

(完整版)算法设计与分析考试题及答案,推荐文档

(完整版)算法设计与分析考试题及答案,推荐文档
法好坏的标准是______________________。 3.某一问题可用动态规划算法求解的显著特征是
____________________________________。 4.若序列 X={B,C,A,D,B,C,D},Y={A,C,B,A,B,D,C,D},请给出序列
X 和 Y 的一个最长公共子序列_____________________________。 5.用回溯法解问题时,应明确定义问题的解空间,问题的解空间至

之分。
5、 f(n)= 6×2n+n2,f(n)的渐进性态 f(n)= O(
)
6、 贪心算法总是做出在当前看来
的选择。也就是说贪心算法并不从整体最优考
虑,它所做出的选择只是在某种意义上的

7、 许多可以用贪心算法求解的问题一般具有 2 个重要的性质:
性质和
性质。
二、简答题(本题 25 分,每小题 5 分)
五、算法理解题(本题 5 分) 设有 n=2k 个运动员要进行循环赛,
现设计一个满足以下要求的比赛日程表:
①每个选手必须与其他 n-1 名选手比赛各一次; ②每个选手一天至多只能赛一次;
③循环赛要在最短时间内完成。
我去(人1)如也果 就n=2k有,循人环赛!最少为需要U进R行扼几天腕; 入站内信不存在向你偶同意调剖沙 (2)当 n=23=8 时,请画出循环赛日程表。
六、算法设计题(本题 15 分) 分别用贪心算法、动态规划法、回溯法设计 0-1 背包问题。要求:说明所使用的算法
策略;写出算法实现的主要步骤;分析算法的时间。 七、算法设计题(本题 10 分)
建议收藏下载本文,以便随时学习! 通过键盘输入一个高精度的正整数 n(n 的有效位数≤240),去掉其中任意 s 个数字后, 剩下的数字按原左右次序将组成一个新的正整数。编程对给定的 n 和 s,寻找一种方案, 使得剩下的数字组成的新数最小。 【样例输入】 178543 S=4 【样例输出】 13
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一、什么是最优二叉查找树最优二叉查找树:给定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,生成的两个二叉查找树的例子。

图二就是在这种情况下一棵最优二叉查找树。

概率分布:i012345p i0.150.100.050.100.20q i0.050.100.050.050.050.10已知每个关键字以及虚拟键被搜索到的概率,可以计算出一个给定二叉查找树内一次搜索的期望代价。

假设一次搜索的实际代价为检查的节点的个数,即所发现的节点的深度加1.计算一次搜索的期望代价等式为:建立一棵二叉查找树,如果是的上式最小,那么这棵二叉查找树就是最优二叉查找树。

而且有下式成立:二、最优二叉查找树的最优子结构最优子结构:如果一棵最优二叉查找树T有一棵包含关键字ki,..,kj的子树T',那么这可子树T'对于关键字Ki,...,kj和虚拟键di-1,...dj的子问题也必定是最优的。

可以应用剪贴法证明。

根据最优子结构,寻找最优解:给定关键字ki,...,kj,假设kr(i<=r<=j)是包含这些键的一棵最优子树的根。

其左子树包含关键字ki,...,kr-1和虚拟键di-1,...,dr-1,右子树包含关键字kr+1,...,kj和虚拟键dr,...dj。

我们检查所有的候选根kr,就保证可以找到一棵最优二叉查找树。

递归解:定义e[i,j]为包含关键字ki,...,kj的最优二叉查找树的期望代价,最终要计算的是e[1,n]。

当j = i - 1时,此时子树中只有虚拟键,期望搜索代价为e[i,i - 1] = qi-1.当j >= i时,需要从ki,...,kj中选择一个根kr,然后分别构造其左子树和右子树。

下面需要计算以kr为根的树的期望搜索代价。

然后选择导致最小期望搜索代价的kr做根。

现在需要考虑的是,当一棵树成为一个节点的子树时,期望搜索代价怎么变化?子树中每个节点深度都增加1.期望搜索代价增加量为子树中所有概率的总和。

对一棵关键字ki,...,kj的子树,定义其概率总和为:因此,以kr为根的子树的期望搜索代价为:而因此e[i,j]可以进一步写为:这样推导出最终的递归公式为:三、代码实现(C++):[cpp]view plaincopyprint?1.//最优二叉查找树2.3.#include <iostream>4.ing namespace std;6.7.const int MaxVal = 9999;8.9.const int n = 5;10.//搜索到根节点和虚拟键的概率11.double p[n + 1] = {-1,0.15,0.1,0.05,0.1,0.2};12.double q[n + 1] = {0.05,0.1,0.05,0.05,0.05,0.1};13.14.int root[n + 1][n + 1];//记录根节点15.double w[n + 2][n + 2];//子树概率总和16.double e[n + 2][n + 2];//子树期望代价17.18.void optimalBST(double *p,double *q,int n)19.{20.//初始化只包括虚拟键的子树21.for (int i = 1;i <= n + 1;++i)22. {23. w[i][i - 1] = q[i - 1];24. e[i][i - 1] = q[i - 1];25. }26.27.//由下到上,由左到右逐步计算28.for (int len = 1;len <= n;++len)29. {30.for (int i = 1;i <= n - len + 1;++i)31. {32.int j = i + len - 1;33. e[i][j] = MaxVal;34. w[i][j] = w[i][j - 1] + p[j] + q[j];35.//求取最小代价的子树的根36.for (int k = i;k <= j;++k)37. {38.double temp = e[i][k - 1] + e[k + 1][j] + w[i][j];39.if (temp < e[i][j])40. {41. e[i][j] = temp;42. root[i][j] = k;43. }44. }45. }46. }47.}48.49.//输出最优二叉查找树所有子树的根50.void printRoot()51.{52. cout << "各子树的根:" << endl;53.for (int i = 1;i <= n;++i)54. {55.for (int j = 1;j <= n;++j)56. {57. cout << root[i][j] << " ";58. }59. cout << endl;60. }61. cout << endl;62.}63.64.//打印最优二叉查找树的结构65.//打印出[i,j]子树,它是根r的左子树和右子树66.void printOptimalBST(int i,int j,int r)67.{68.int rootChild = root[i][j];//子树根节点69.if (rootChild == root[1][n])70. {71.//输出整棵树的根72. cout << "k" << rootChild << "是根" << endl;73. printOptimalBST(i,rootChild - 1,rootChild);74. printOptimalBST(rootChild + 1,j,rootChild);75.return;76. }77.78.if (j < i - 1)79. {80.return;81. }82.else if (j == i - 1)//遇到虚拟键83. {84.if (j < r)85. {86. cout << "d" << j << "是" << "k" << r << "的左孩子" << endl;87. }88.else89. cout << "d" << j << "是" << "k" << r << "的右孩子" << endl;90.return;91. }92.else//遇到内部结点93. {94.if (rootChild < r)95. {96. cout << "k" << rootChild << "是" << "k" << r << "的左孩子" << endl;97. }98.else99. cout << "k" << rootChild << "是" << "k" << r << "的右孩子" << endl;100. }101.102. printOptimalBST(i,rootChild - 1,rootChild);103. printOptimalBST(rootChild + 1,j,rootChild);104.}105.106.int main()107.{108. optimalBST(p,q,n);109. printRoot();110. cout << "最优二叉树结构:" << endl;111. printOptimalBST(1,n,-1);112.}我们将表e、w以及root旋转45°,便于查看上述程序的计算过程。

上述代码核心在于函数optimalBST,其计算顺序是从下到上、从左到右。

首先是依据概率数组pi、qi初始化:给最下面的一行赋值。

然后是三个for循环:从下到上计算表中每一行的值,可以充分利用前面计算出来的结果。

如果每当计算e[i][j]的时候都从头开始计算w[i][j],那么需要O(j-i)步加法,但是将这些值保存在表w[1...n+1][0...n]中,就避免这些复杂的计算。

输出结果:。

相关文档
最新文档