最优二叉查找树
- 格式:pdf
- 大小:66.25 KB
- 文档页数:3
《数据结构》习题库之三:判断题1. 程序就是算法,但算法不一定是程序。
()2. 线性表只能采用顺序存储结构或者链式存储结构。
()3. 线性表的链式存储结构是通过指针来间接反映数据元素之间逻辑关系的。
()4. 除插入和删除操作外,数组的主要操作还有存取、修改、检索和排序等。
()5. 稀疏矩阵中0元素的分布有规律,因此可以采用三元组方法进行压缩存储。
()6. 不管堆栈采用何种存储结构,只要堆栈不空,可以任意删除一个元素。
()7. 确定串T在串S中首次出现的位置的操作称为串的模式匹配。
()8. 深度为h的非空二叉树的第i层最多有2i-1 个结点。
()9. 满二叉树就是完全二叉树。
()10. 已知一棵二叉树的前序序列和后序序列可以唯一地构造出该二叉树。
()11. 非空二叉排序树的任意一棵子树也是二叉排序树。
()12. 对一棵二叉排序树进行前序遍历一定可以得到一个按值有序的序列。
()13. 若有向图G=(V,E)的拓扑序列不唯一,则图中必须有两条弧和。
()14. 散列表的查找效率主要取决于所选择的散列函数与处理冲突的方法。
()15. 序列初始为逆序时,泡排序法所进行的元素之间的比较次数最多。
()16. 算法一定要有输入和输出。
()17. 算法分析的目的旨在分析算法的效率以求改进算法。
()18. 非空线性表中任意一个数据元素都有且仅有一个直接后继元素。
()19. 数据的存储结构不仅有顺序存储结构和链式存储结构,还有索引结构与散列结构。
()20. 线性链表中各个链结点之间的地址不一定要连续。
()21. 若频繁地对线性表进行插入和删除操作,该线性表采用顺序存储结构更合适。
()22. 若线性表采用顺序存储结构,每个数据元素占用4个存储单元,第12个数据元素的存储地址为144,则第1个数据元素的存储地址是101。
()23. 若长度为n的线性表采用顺序存储结构,删除表的第i个元素之前需要移动表中n-i+1个元素。
()24. 符号link(p)出现在表达式中表示p所指的那个结点的内容。
动态规划-最优⼆叉搜索树摘要: 本章介绍了⼆叉查找树的概念及操作。
主要内容包括⼆叉查找树的性质,如何在⼆叉查找树中查找最⼤值、最⼩值和给定的值,如何找出某⼀个元素的前驱和后继,如何在⼆叉查找树中进⾏插⼊和删除操作。
在⼆叉查找树上执⾏这些基本操作的时间与树的⾼度成正⽐,⼀棵随机构造的⼆叉查找树的期望⾼度为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.二叉排序树定义二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:(1)若左子树不空,则左子树上所有结点的值均小于根结点的值;若右子树不空,则右子树上所有结点的值均大于根结点的值。
(2)左右子树也都是二叉排序树,如图6-2所示。
2.二叉排序树的查找过程由其定义可见,二叉排序树的查找过程为:(1)若查找树为空,查找失败。
(2)查找树非空,将给定值key与查找树的根结点关键码比较。
(3)若相等,查找成功,结束查找过程,否则:①当给值key小于根结点关键码,查找将在以左孩子为根的子树上继续进行,转(1)。
②当给值key大于根结点关键码,查找将在以右孩子为根的子树上继续进行,转(1)。
3.二叉排序树插入操作和构造一棵二叉排序树向二叉排序树中插入一个结点的过程:设待插入结点的关键码为key,为将其插入,先要在二叉排序树中进行查找,若查找成功,按二叉排序树定义,该插入结点已存在,不用插入;查找不成功时,则插入之。
因此,新插入结点一定是作为叶子结点添加上去的。
构造一棵二叉排序树则是逐个插入结点的过程。
对于关键码序列为:{63,90,70,55,67,42,98,83,10,45,58},则构造一棵二叉排序树的过程如图6-3所示。
4.二叉排序树删除操作从二叉排序树中删除一个结点之后,要求其仍能保持二叉排序树的特性。
设待删结点为*p(p为指向待删结点的指针),其双亲结点为*f,删除可以分三种情况,如图6-4所示。
(1)*p结点为叶结点,由于删去叶结点后不影响整棵树的特性,所以,只需将被删结点的双亲结点相应指针域改为空指针,如图6-4(a)所示。
(2)*p结点只有右子树或只有左子树,此时,只需将或替换*f结点的*p子树即可,如图6-4(b)、(c)所示。
(3)*p结点既有左子树又有右子树,可按中序遍历保持有序地进行调整,如图6-4(d)、(e)所示。
设删除*p结点前,中序遍历序列为:① P为F的左子女时有:…,Pi子树,P,Pj,S子树,Pk,Sk子树,…,P2,S2子树,P1,S1子树,F,…。
红黑树优点和应用场景红黑树优点及应用场景红黑树是一种自平衡二叉查找树,具有优秀的平均性能和较稳定的最坏情况性能。
它的优点包括高效的查找、插入和删除操作,以及平衡性能好,不易退化成链表等。
本文将详细介绍红黑树的优点及其应用场景。
一、红黑树优点1.高效的查找、插入和删除操作红黑树的查找、插入和删除操作时间复杂度均为O(log n),其中n 为树中节点数。
由于红黑树的平衡性,它的查找性能非常高,并且插入和删除操作也能够保持比较稳定的性能。
这使得红黑树在实际应用中非常受欢迎。
2.平衡性能好,不易退化成链表红黑树是一种自平衡二叉查找树,它通过保持节点的颜色、旋转等方式来保持树的平衡性。
这使得红黑树不易退化成链表,从而保证了它的查找、插入和删除操作的时间复杂度稳定。
3.易于实现和理解红黑树的实现相对简单,且易于理解。
它的平衡性质也使得它的实现相对稳定,不易出现错误。
这使得红黑树在实际应用中非常受欢迎,尤其是对于那些需要高效查找、插入和删除操作的应用场景。
二、红黑树应用场景1.数据库索引数据库索引是一种常见的应用场景,它通过建立一棵树来实现对数据的快速查找。
在这种情况下,红黑树是一种非常合适的数据结构,因为它能够提供高效的查找、插入和删除操作,并且能够保持较好的平衡性。
2.操作系统调度操作系统调度是一种需要高效查找和插入操作的应用场景。
在这种情况下,红黑树可以用来实现进程优先级的管理,从而保证高优先级进程的优先执行。
3.路由表路由表是一种需要高效查找和插入操作的应用场景。
在这种情况下,红黑树可以用来实现路由表的管理,从而保证路由的高效查找和更新操作。
4.编译器实现编译器是一种需要高效查找和插入操作的应用场景。
在这种情况下,红黑树可以用来实现符号表的管理,从而保证编译器的高效性能。
红黑树是一种非常优秀的数据结构,它具有高效的查找、插入和删除操作,以及平衡性能好,不易退化成链表等优点。
它在数据库索引、操作系统调度、路由表和编译器实现等应用场景中都有广泛的应用。
介绍二叉排序树的结构和特点二叉排序树,也称为二叉搜索树或二叉查找树,是一种特殊的二叉树结构,其主要特点是左子树上的节点都小于根节点,右子树上的节点都大于根节点。
在二叉排序树中,每个节点都存储着一个关键字,而且所有的关键字都不相同。
二叉排序树的结构如下:1.根节点:二叉排序树的根节点是整个树的起始点,其关键字是最大的。
2.左子树:根节点的左子树包含着小于根节点关键字的所有节点,且左子树本身也是一个二叉排序树。
3.右子树:根节点的右子树包含着大于根节点关键字的所有节点,且右子树本身也是一个二叉排序树。
二叉排序树的特点如下:1.有序性:二叉排序树的最重要特点是有序性。
由于左子树上的节点都小于根节点,右子树上的节点都大于根节点,所以通过中序遍历二叉排序树,可以得到一个有序的序列。
2.快速查找:由于二叉排序树是有序的,所以可以利用二叉排序树进行快速查找操作。
对于给定的关键字,可以通过比较关键字与当前节点的大小关系,逐步缩小查找范围,最终找到目标节点。
3.快速插入和删除:由于二叉排序树的有序性,插入和删除操作比较简单高效。
插入操作可以通过比较关键字的大小关系,找到合适的位置进行插入。
删除操作可以根据不同情况,分为三种情况处理:删除节点没有子节点、删除节点只有一个子节点和删除节点有两个子节点。
4.可以用于排序:由于二叉排序树的有序性,可以利用二叉排序树对一组数据进行排序。
将数据依次插入二叉排序树中,然后再通过中序遍历得到有序序列。
二叉排序树的优缺点如下:1.优点:(1)快速查找:通过二叉排序树可以提供快速的查找操作,时间复杂度为O(log n)。
(2)快速插入和删除:由于二叉排序树的有序性,插入和删除操作比较简单高效。
(3)可以用于排序:通过二叉排序树可以对一组数据进行排序,时间复杂度为O(nlog n)。
2.缺点:(1)受数据分布影响:如果数据分布不均匀,可能导致二叉排序树的高度增加,从而降低了查找效率。
(2)不适合大规模数据:对于大规模数据,二叉排序树可能会导致树的高度过高,从而影响了查找效率。
平衡树——特点:所有结点左右子树深度差≤1排序树——特点:所有结点―左小右大字典树——由字符串构成的二叉排序树判定树——特点:分支查找树(例如12个球如何只称3次便分出轻重)带权树——特点:路径带权值(例如长度)最优树——是带权路径长度最短的树,又称Huffman树,用途之一是通信中的压缩编码。
1.1 二叉排序树:或是一棵空树;或者是具有如下性质的非空二叉树:(1)若左子树不为空,左子树的所有结点的值均小于根的值;(2)若右子树不为空,右子树的所有结点均大于根的值;(3)它的左右子树也分别为二叉排序树。
例:二叉排序树如图9.7:二叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构。
中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。
每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。
搜索,插入,删除的复杂度等于树高,期望O(logn),最坏O(n)(数列有序,树退化成线性表).虽然二叉排序树的最坏效率是O(n),但它支持动态查询,且有很多改进版的二叉排序树可以使树高为O(logn),如SBT,AVL,红黑树等.故不失为一种好的动态排序方法.2.2 二叉排序树b中查找在二叉排序树b中查找x的过程为:1. 若b是空树,则搜索失败,否则:2. 若x等于b的根节点的数据域之值,则查找成功;否则:3. 若x小于b的根节点的数据域之值,则搜索左子树;否则:4. 查找右子树。
[cpp]view plaincopyprint?1.Status SearchBST(BiTree T, KeyType key, BiTree f, BiTree &p){2. //在根指针T所指二叉排序樹中递归地查找其关键字等于key的数据元素,若查找成功,3. //则指针p指向该数据元素节点,并返回TRUE,否则指针P指向查找路径上访问的4. //最好一个节点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL5. if(!T){ p=f; return FALSE;} //查找不成功6. else if EQ(key, T->data.key) {P=T; return TRUE;} //查找成功7. else if LT(key,T->data.key)8. return SearchBST(T->lchild, key, T, p); //在左子树继续查找9. else return SearchBST(T->rchild, key, T, p); //在右子树继续查找10.}2.3 在二叉排序树插入结点的算法向一个二叉排序树b中插入一个结点s的算法,过程为:1. 若b是空树,则将s所指结点作为根结点插入,否则:2. 若s->data等于b的根结点的数据域之值,则返回,否则:3. 若s->data小于b的根结点的数据域之值,则把s所指结点插入到左子树中,否则:4. 把s所指结点插入到右子树中。
第八节最优二叉树(哈夫曼树)一、概念在具有n个带权叶结点的二叉树中,使所有叶结点的带权路径长度之和(即二叉树的带权路径长度)为最小的二叉树,称为最优二叉树(又称最优搜索树或哈夫曼树),即最优二叉树使(W k—第k个叶结点的权值;P k—第k个叶结点的带权路径长度)达到最小。
二、最优二叉树的构造方法假定给出n个结点ki(i=1‥n),其权值分别为Wi(i=1‥n)。
要构造以此n个结点为叶结点的最优二叉树,其构造方法如下:首先,将给定的n个结点构成n棵二叉树的集合F={T1,T2,……,Tn}。
其中每棵二叉树Ti中只有一个权值为wi的根结点ki,其左、右子树均为空。
然后做以下两步⑴在F中选取根结点权值最小的两棵二叉树作为左右子树,构造一棵新的二叉树,并且置新的二叉树的根结点的权值为其左、右子树根结点的权值之和;⑵在F中删除这两棵二叉树,同时将新得到的二叉树加入F 重复⑴、⑵,直到在F中只含有一棵二叉树为止。
这棵二叉树便是最优二叉树。
三、最优二叉树的数据类型定义在最优二叉树中非叶结点的度均为2,为满二叉树,因此采用顺序存储结构为宜。
如果带权叶结点数为n个,则最优二叉树的结点数为2n-1个。
Const n=叶结点数的上限;m=2*n-1;{最优二叉树的结点数}Typenode=record{结点类型}data:<数据类型>;{权值}prt,lch,rch,lth:0‥m;{父指针、左、右指针和路径长度}end;wtype=array[1‥n] of <数据类型> ;{n个叶结点权值的类型}treetype=array[1‥m] of node;{最优二叉树的数组类型}Var tree:treetype;{其中tree [1‥n]为叶结点,tree [n+1‥2n-1]为中间结点,根为tree [2n-1]}四、构造最优二叉树的算法。
最优二叉树规则最优二叉树,也称为哈夫曼树,是一种特殊的二叉树结构,它的构建过程是基于一组权值的频率分布来进行的。
最优二叉树规则是指在构建最优二叉树时所遵循的一些基本规则,这些规则可以帮助我们更好地理解最优二叉树的构建过程,从而更好地应用它们来解决实际问题。
最优二叉树的构建过程最优二叉树的构建过程是基于一组权值的频率分布来进行的。
在构建最优二叉树时,我们需要按照以下步骤进行:1. 将所有的权值按照从小到大的顺序排列。
2. 选取两个权值最小的节点作为左右子节点,构建一个新的节点,其权值为这两个节点的权值之和。
3. 将新节点的权值插入到原来的序列中,并将原来的两个节点从序列中删除。
4. 重复步骤2和3,直到序列中只剩下一个节点为止。
最优二叉树规则在构建最优二叉树时,我们需要遵循以下规则:1. 权值越大的节点应该离根节点越近。
2. 在同一层次上,权值越小的节点应该在左边。
3. 在构建最优二叉树时,我们应该尽量使得树的深度最小。
这些规则的目的是为了使得最优二叉树的结构更加紧凑,从而减少树的深度,提高树的搜索效率。
在实际应用中,我们可以根据这些规则来构建最优二叉树,从而更好地解决实际问题。
最优二叉树的应用最优二叉树在实际应用中有着广泛的应用,例如在数据压缩、编码和解码、图像处理等领域中都有着重要的应用。
在数据压缩中,我们可以利用最优二叉树来构建哈夫曼编码,从而将数据压缩到最小的空间。
在编码和解码中,我们可以利用最优二叉树来实现高效的编码和解码算法。
在图像处理中,我们可以利用最优二叉树来实现图像的压缩和解压缩,从而减少图像的存储空间和传输带宽。
总结最优二叉树是一种特殊的二叉树结构,它的构建过程是基于一组权值的频率分布来进行的。
在构建最优二叉树时,我们需要遵循一些基本规则,例如权值越大的节点应该离根节点越近,权值越小的节点应该在左边等。
最优二叉树在实际应用中有着广泛的应用,例如在数据压缩、编码和解码、图像处理等领域中都有着重要的应用。
树、⼆叉树、查找算法总结树的定义形式化定义树:T={D,R }。
D是包含n个结点的有限集合(n≥0)。
当n=0时为空树,否则关系R满⾜以下条件:l 有且仅有⼀个结点d0∈D,它对于关系R来说没有前驱结点,结点d0称作树的根结点。
l 除根结点外,每个结点有且仅有⼀个前驱结点。
l D中每个结点可以有零个或多个后继结点。
递归定义树是由n(n≥0)个结点组成的有限集合(记为T)。
其中:l 如果n=0,它是⼀棵空树,这是树的特例;l 如果n>0,这n个结点中存在⼀个唯⼀结点作为树的根结点(root),其余结点可分为m (m≥0)个互不相交的有限⼦集T1、T2、…、Tm,⽽每个⼦集本⾝⼜是⼀棵树,称为根结点root的⼦树。
ð 树中所有结点构成⼀种层次关系!树的基本术语度结点的度:⼀个结点的⼦树的个数树的度:各节点的度的最⼤值。
通常将度为m的树成为m次树或m叉树结点分⽀结点:度不为0的结点(也称⾮终端结点)度为1的结点成为单分⽀结点,度为2的结点称为双分⽀结点叶结点:度为0的结点路径与路径长度路径:两个结点di和dj的结点序列(di,di1,di2,…,dj)。
其中<dx,dy>是分⽀。
路径长度:等于路径所通过的结点数⽬减1(即路径上的分⽀数⽬)结点的层次和树⾼度层次:根结点层次为1,它的孩⼦结点层次为2。
以此类推。
树的⾼度(深度):结点中的最⼤层次;有序树和⽆序树有序树:若树中各结点的⼦树是按照⼀定的次序从左向右安排的,且相对次序是不能随意变换的⽆序树:和上⾯相反森林只要把树的根结点删去就成了森林。
反之,只要给n棵独⽴的树加上⼀个结点,并把这n棵树作为该结点的⼦树,则森林就变成了⼀颗树。
树的性质性质1:树中的结点数等于所有结点的度数之和加1。
证明:树的每个分⽀记为⼀个度,度数和=分⽀和,⽽再给根节点加个分⽀性质2:度为m的树中第i层上⾄多有mi-1个结点(i≥1)。
性质3 ⾼度为h的m次树⾄多有个结点。
最优二叉树规则最优二叉树是一种常见的数据结构,也被称为霍夫曼树。
它的最主要的特点就是能够在动态数据中快速定位元素的位置,从而进行高效的操作。
接下来,我们将分步骤阐述“最优二叉树规则”。
一、概念介绍最优二叉树是一种满足加权路径长度最小的二叉树。
其中,加权路径长度定义为树中所有叶节点的深度乘以其权重,并且具有公式WPL= ∑(i=1,n) w(i) * d(i),其中w(i) 代表叶子节点的权重,d(i)代表叶子节点深度。
二、构建步骤1.首先将所有节点按照权重从大到小排序,构造一个包含所有节点的森林,每个节点在单独的树上。
2.从森林中选取两棵根节点权重最小的树进行合并,节点之间添加一个新的父节点,并将权重设置为子节点权重之和。
3.将新生成的树插入到森林中,删除原有两个节点的树。
4.重复上述合并操作,直到森林中只有一棵树,此时形成的树就是最优二叉树。
三、示例分析例如,现在有集合S={A:7,B:8,C:2,D:5},每个元素及其权重如表所示,我们将使用以上核心步骤来构建其最优二叉树。
1.首先将所有节点按照权重从大到小排序。
C:2,D:5,A:7,B:82.从森林中选取两棵根节点权重最小的树进行合并。
C:2------>----------(1)---------D:5------> A:7--------(3)---------B:8-----3.继续合并子树,形成最底层的新树。
C:2-------> (1)-------(4)---------------(2)--------- A:7-------- B:8--------D:5-------- C:2-------- 最终,我们成功地构建了一个最优二叉树,其中所有的元素在树中的分布符合“贪心算法”的规律,保证了在查找、插入、删除元素时的效率。
总结:最优二叉树规则包含了四个关键步骤:排序,树的合并,新父节点的添加,以及循环合并。
最优二叉搜索树一、问题描述给定一个有序序列K={k1<k2<k3<,……,<kn}和他们被查询的概率P={p1,p2,p3,……,pn},要求构造一棵二叉查找树T,使得查询所有元素的总的代价最小。
二、解题思路最优二叉搜索树问题具有最优子结构性质。
证明:设Tij是有序集{xi,…,xj}关于存取概率分布(ai-1,bi,…,bj,aj)的一棵最优二叉搜索树,平均路长为pij。
Tij的根结点存储元素xk。
其左右子树Tl和Tr的平均路长分别为pl和pr。
由于Tl是关于集合{xi,…,xk-1}的一个二叉搜索树,故pl≥pi,k-1。
如果pl>pi,k-1,那么用Ti,k-1替换Tl可得到平均路长比Tij更小的二叉搜索树。
这与Tij是最优二叉搜索树相矛盾。
所以,左子树Tl是一棵最优二叉搜索树,同理可证右子树Tr也是一棵最优二叉搜索树,即最优二叉搜索树的子树也是最优二叉搜索树。
建立递归关系式若最优二叉搜索树Ti,j的根结点为k,最小平均路长为pi,j,m[i,j]表示Ti,j的开销,则有m[i,j]=wi,jpi,j,其中<E:\2008学术交流\200学术交流第四卷第八期(2008总第35期)\第1次96篇\1.3软件设计开发\tr01.tif>,可建立下列递归关系:M[i,j]=bk+(m[i,k-1]+wi,k-1)+ (m[k+1,j]+wk+1,j)而 wi,j=bk+wi,k-1+wk+1,j则m[i,j]=wi,j+m[i,k-1]+m[k+1,j](1)将k=i+1,i+2,…,j分别代入<1>式,选取使m[i,j]达到最小的K,这样递归关系式改为:m[i,j]=wi,j+min{m[i,k-1]+m[k+1,j]}m[i,i-1]=0,1≤i≤n解递归关系,m[1,n]就是所求的最优值。
将对应于m[i,j]的断开位置k 记录在s[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。
下面来求解T(n):
定义函数f(x) = T(0) + T(1)*x + T(2)*x2 + ......
那么有:
f(x)2 = (T(0)2) + (T(0)T(1) + T(1)T(0)) · x + (T(0)T(2) + T(1)T(1) + T(2)T(0)) · x2 + ......
= T(1) + T(2) · x + T(3) · x2 + ......
= (f(x) - T(0)) / x
= (f(x) - 1) / x
这样解方程得到f(x) = [1 - (1 - 4x)1/2] / 2x
右边进行泰勒展开,再与定义式比较最终得到:T(n) = (2n)! / (n!(n+1)!)
然后根据Stirling公式:n! ~(2πn)1/2*(n/e)n
于是有(2n)! / n!(n+1)! ~(4n1/2*2n2n) / (2n1/2*nn*(2(n+1))1/2*(n+1)n)
~4n*(n+1)-3/2*(n/(n+1))n ~4n*n-3/2
因此最后得到穷举方法构造最优二叉查找树的时间复杂度:T(n) = O(4n*n-3/2)
2、递归
实际上左右子树是互不影响的,不需要穷举所有左右子树的组合,所以不需要用乘法原理,加法原理就可以了,这样式子变为:
T(n) = T(0) + T(n-1) + T(1) + T(n-2) + T(2) + T(n-3) + ...... + T(n-2) + T(1) + T(n-1) + T(0) = 2(T(0) + T(1) + T(2) + ...... + T(n-1))
= 3T(n-1)
所以得到T(n) = O(3n),还是指数级的一个算法
3、动态规划
上面得到指数级算法的原因在于,计算了很多重复的子树情况,一些子树的查找代价被计算了很多遍;而一棵树如果是最优二叉搜索树,那么要么它是空树,要么它的左、右子树也是最优二叉搜索树,因此只需要将子树的查找代价记录下来,采用记忆化搜索或者是自底向上的动态规划的方法,虽然需要消耗一定的空间,但可以把时间复杂度从指数级降到多项式级,这些空间消耗也是可以接受的。
以下是采用自底向上的解法:
输入:键值序列K = <k1, k2, . . . , kn>,概率序列P = <p1, p2, . . . , pn>
输出:两个二维数组,Price[i][j]表示ki到kj构成的最优子树的查找代价,Root[i][j]表示表示ki到kj构成的最优子树的根节点位置(用于重构最优二叉查找树)
算法1:
For 子树大小size = 1 to n
For 子树的起点start = 1 to (n - size + 1) //这样子树的终点即为end = start + size -
1,长度为size
For 该子树的所有节点作为根节点root = start to end
对于每个root,根据之前计算过的Price数组得到左右最优子树的代价,可直接得到该子树的代价price为:左右子树的最优子树代价之和+ 所有节点的访问概率之和(因为所有节点都下降了一层)在内层循环中找到代价最小的price和root分别记录在Price[start][end]和Root[start][end]中。
下面分析这个算法的时间复杂度:
由于除了计算出我们最后需要得到的Price和Root二维数组,还产生了部分冗余的子树,因此不能简单的将算法归结为O(n2)的算法。
对于子树大小为1时,我们考察了n个子树;
对于子树大小为2时,一共产生了(n - 1)个最优子树,但是在我们的每次考察中,都将子树的所有节点作为根节点考虑过一次,因此每得到1个大小为2的子树,我们需要考察2个不同的子树来找到一个代价最小的,因此最后我们实际上考察了2(n - 1)个子树;
对于子树大小为3时,类似的,我们考察了3(n - 2)个子树……
对于子树大小为n时,我们考察了n个子树。
最后,我们一共考察了T(n) = n + 2(n - 1) + 3(n - 2) + ...... + n个子树。
求解这个公式依然可以借用之前的方法,定义函数f(x) = 1 + 2x + 3x2 + ...... = (1 - x)-2 这样一来f(x)2 = T(1) + T(2)*x + T(3)*x2 + ......
再借用泰勒展开得到T(n) = (n + 2)(n + 1)n/6 = O(n3),或者把所有项视为n2,则有T(n) ≤n2 + n2 + n2 + n2 + ...... = (n+1)n2 ≤2n3,或者把中间n/2项都视为n/4*3n/4的话,则有T(n)≥n/2*n/4*3n/4 = (3/32)n3,根据时间复杂度的定义有T(n) = O(n3)。