数据结构与算法分析笔记(c++)_查找树ADT-二叉查找树

二叉树的一个重要的应用是它们在查找中的应用
假设树中的每个结点存储一项数据假设它们是些整数,我们还将假设,所有的项都是互异的。
使二叉树成为二叉查找树的性质是,对于树中的每个结点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有项的值大于x中的项。注意,这意味着,该树所有的元素都可以用某种一致的方式排序。
在这里插入图片描述
右边的树在其项是6的结点(该结点正好是根结点)的左子树中,有一个结点的项是7。
现在给出通常对二叉查找树进行的操作的简要描述。注意,由于树的递归定义,通常是递归地编写这些操作的例程。因为二叉查找树的平均深度是O(logN,所以我们一般不必担心栈空间被用尽。在这里插入图片描述
现在我们可以描述某些 private方法。
1.contains
如果在树T中有项为X的结点,那么 contain操作就返回true,否则,若没有这样的结点就返回false。在这里插入图片描述
注意测试的顺序。关键的问题是首先要对是否为空树进行测试,因为如果不这么做就会产生一个企图通过NUL指针访问数据成员的运行错误。在这里插入图片描述
2.findMin和findMax
这两个 private例程分别返回指向树中包含最小元和最大元的结点的指针。为执行 findmin,从根开始并且只要有左儿子就向左进行。终止点就是最小的元素。findMax朝向右在这里插入图片描述
在这里插入图片描述
注意我们是如何小心地处理空树的退化情况的。虽然这么做总是重要的,但在递归程序中这么做尤其重要。
3.insert
进行插入操作的例程在概念上是很简单的。
样沿着树查找。如果找到X,则什么也不用做(或做一些“更新”)。否则,将X插入到遍历的路径上的最后一点上。图4-22显示了实际的插入情况。在这里插入图片描述
重复元的插入可以通过在结点记录中保留一个附加字段以指示此数据元出现的频率来处理。
图4-23给出了插入例程的代码。第12行和第14行递归地将x插入到适当的子树中。在这里插入图片描述
4.remove
同许多数据结构…样,最困难的操作是删除。一旦发现要被删除的结点,就需要考虑几种可能的情况。
如果结点是一片树叶,那么它可以被立即删除。如果结点有一个儿子,则该结点可以在其父结点调整它的链以绕过该结点后被删除(为了清楚起见,我们将明确地画出链的指向),见图4-24在这里插入图片描述
复杂的情况是处理具有两个儿子的结点。一般的删除策略是用其右子树的最小的数据(很容易找到)代替该结点的数据并递归地删除那个结点(现在它是空的)。因为右了树中的最小的结点不可能有左儿子,所以第二次 remove就很容易。在这里插入图片描述
图4-26中的程序可以完成删除的工作,但其效率并不高,因为它沿该树进行两次搜索以查找和删除右子树中最小的结点。通过写一个特殊的 removeR方法可以容易地改变效率不高的缺点在这里插入图片描述
如果删除的次数不多,则通常使用的策略是懒惰删除(lazy deletion):当一个元素要被删除时,它仍留在树中,而只是做了个被删除的记号。这种做法在有重复项时很流行,因为此时记录出现频率数的数据成员可以减1。如果树中的实际结点数和“被删除”的结点数相同,那么树的深度预计只上升一个小的常数(为什么?),因此,只存在一个与懒惰删除相关的非常小的时间损耗。再有,如果被删除的项又被重新插入,那么分配一个新单元的开销就避免了
5.析构函数和复制赋值操作符
与往常一样,析构函数调用 makeEmpty。公有的 makeEmpty(没有显示出来)则简单地调用私有的递归版本的 makeEmpty。如图4-27所示,在递归地处理t的子树之后,对t调用 delete这样一来,所有的结点就都递归地回收了。注意,在最后,t(此时为root)就改为指向NULL。在图4-28中显示的复制赋值操作符与以往的程序一样,首先调用 makeEmpty来回收内存,然后进行hs的复制。我们使用一个名为 clone的很老套的递归函数来处理这些啰嗦的工作。在这里插入图片描述
我们在本节要证明,如果所有的插入序列都是等可能的,那么,树的所有结点的平均深度为logN
一棵树的所有结点的深度和称为内部路径长(internal path length)。
令D(N)是具有N个结点的某棵树的内部路径长,D(1)=0。一棵N结点树是由一棵i结点左子树和一棵(N-i-1)结点右子树以及深度为0的一个根结点组成,其中0≤i<N,D(i)为根的左子树的内部路径长。但是在原树中,所有这些结点都要加深一层。同样的结论对于右子树也是成立的。
因此我们得到递推关系:在这里插入图片描述
在这里插入图片描述
但是,一上来就断言这个结果意味着上一节讨论的所有操作的平均运行时间是O(logN并不完全正确。原因在于删除操作,我们并不清楚是否所有的二叉査找树都是等可能出现的。特别是上面描述的删除算法有助于使得左子树比右子树深度深,因为我们总是用右子树的一个结点来代替删除的结点。在这里插入图片描述
在没有删除或是使用懒惰删除的情况下,可以证明所有二叉查找树都是等可能出现的,而且可以断言:上述操作的平均运行时间都是O(logN)。
一个称为平衡(balance)的附加的结构条件:任何结点的深度均不得过深有许多一般的算法实现平衡树。但是,大部分算法都要比标准的二叉查找树复杂得多,而且更新要平均花费更长的时间。不过,它们确实防止了处理起来非常麻烦的一些简单情形。下面将介绍最老的一种平衡查找树,即AVL树。
另外,较新的方法是放弃平衡条件,允许树有任意的深度,但是在每次操作之后要使用一个调整规则进行调整,使得后面的操作效率更高。这种类型的数据结构一般属于自调整(self-adjusting)类结构。在二又查找树的情况下,对于任意单个运算我们不再保证O(logN)的时间界,但是可以证明任意连续M次操作在最坏的情形下花费时间 O(M log.n。一般这足以防止令人棘手的最坏情形。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值