7.5 二叉查找树(BST)及平衡二叉树(AVL)的概念及基本运算

经历二叉树的重重拷问,终于来到二叉查找树,又称二叉搜索树(BinarySearchTrees)。在第6章查找算法的介绍中,我们提到了树表查找/动态表查找,BST因为关键字有序,在查找时同样高效,并且易于对关键字的插入及删除操作。

二叉搜索树的基本概念

二叉搜索树是一棵二叉树。这样一棵树可以使用一个链表结构表示,其中每个结点就是一个对象。除了结点中的关键字外,每个结点还包含属性left、right、p,它们分别指向结点的左孩子、右孩子和双亲。如果某个孩子结点和父结点不存在,则相应属性的值为null。根结点是树中唯一父结点为null的结点。

二叉搜索树的定义为:二叉搜索树或是空树,或者是满足如下性质的二叉树:

1. 若它的左子树非空,则左子树上所有记录的值均小于根记录的值;

2. 若它的右子树非空,则右子树上所有记录的值均大于根记录的值;

3. 左、右子树本身又各是一颗二叉搜索树。

在实际上,关键字有可能重复,小于则改为小于等于,大于则改为大于等于。

从BST的性质可推出二叉搜索树的另一个重要性质:按中序遍历该树所得到的序列是一个递增的有序序列。
 

二叉树搜索树的基本运算

可参考几个博客:

搜索算法—二叉搜索树

十分钟带你了解二叉搜索树(BST)!

二叉搜索树支持许多动态集合操作,包括SEARCH(查找指定结点)、MINIMUM(最小关键字结点)、MAXMUM(最大关键字结点)、PREDECESSOR(结点的先驱)、SUCCESSOR(结点的后继)、INSERT(结点的插入)和DELETE(结点的删除)等。因此,我们使用一棵搜索树既可以作为一个字典又可以作为一个优先队列。

二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。对于有n个结点的一棵完全二叉树来说,这些操作的最坏运行时间为O(log n)。然而,如果这棵树是一条n 个结点组成的线性链,那么同样的操作就要花费O(n)的最坏运行时间。

二叉搜索树最基本的操作是 查找,插入,删除

查找: 如果想查找某个数字X,从根节点开始比较。如果X比根节点大,则去与根节点的右节点比较,如此类推,直到找到X(或子节点为空)为止。比较基础,不再列代码。

插入:与查找类似,设新节点的key为X,从根节点开始比较。如果X比根节点大,则去与根节点的右节点比较。如此类推,如果找到了某个节点的key与X相同,覆盖这个节点;如果没找到,则根据最后一次比较结果,插到最后一次比较的节点的左节点或右节点。即按照查找的方法找到一个结点,插入到它左或右。

删除:删除一个记录,不能把该记录及其子树都删除,而是只删其本身,还要保证删除后仍保持BST的性质。删除操作为3种情况:

  1. 待删结点是叶子结点,此情况最为简单,直接删除这个节点即可。

  2. 待删结点只有左子树或右子树,删除这个结点只需把这个子树的根结点和这个结点的父结点相连即可。

  3. 待删结点同时有左子树和右子树,可以从其左子树中选择值最大的结点或从其右子树选择值最小的结点,放在被删结点的位置上。以前者为例,待删结点的位置上要放置其左子树值最大的,也就是左子树的右下结点,(如下图左,要删除5,把4放在该位置上),需要注意,该结点若有左子树(一定没有右子树哈),把其左子树放在移走的结点上(上文说的4)。

 

二叉搜索树最坏情况
二叉搜索树最坏情况

 

平衡二叉树的概念

平衡二叉树是二叉搜索树应用的根本,虽然BST各种操作的平均时间均为O(log n),但最坏的情况下,操作时间均会达到O(n)。(试想我们对一个BST从1开始依次插入数字2,3,4,5,6,此时树是什么样的,也就是上图右边)。

为了避免这种情况,人们研究了 许多动态平衡的方法,使得往树中插入或删除记录时,通过调整树的形态来保持树的“平衡”,不仅维持BST的性质,而且确保各种操作的最坏情况时间均为O(log n)。其中最为著名的是AVL树。

若一棵二叉树中每个结点的左右子树的高度至多相差1,则称此二叉树为平衡二叉树。在算法中,通过平衡因子bf(balance factor) 来具体实现。 平衡因子的定义是: 平衡二叉树中每个结点有一个平衡因子域,每个结点的平衡因子是该结点左子树的高度减去右子树的高度。平衡二叉树的平衡因子只能为(-1,0,1)。

AVL与平衡因子
AVL与平衡因子

平衡二叉树的基本操作

接下来是核心内容了,AVL的基本运算仍然是查找,插入和删除,由于AVL对于平衡的特殊要求,其操作也略微复杂而已。下面我们一一介绍。

此处我们仍推荐几篇博客:

二叉查找树与平衡二叉树

震惊!!!平衡二叉树居然还有这种学习方法!!

鄙人根据博客、教材等简要记录最核心的知识,若不甚理解,还需多看一些资料和讲解。

 

平衡二叉树结点插入:

若向AVL中插入一个新的结点后破坏了其平衡性,我们需要对AVL进行调整:

1. 从新插入结点向上,查找第一个失去平衡的结点A;

2. 确定调整类型,这是我们理解四类调整方法的根本, LL,RR,LR,RL。 结点A失衡是由于在A的孩子B的子树插入结点造成的,其调整类型就为LL;若结点A失衡是由于在A的左孩子B的右子树插入结点造成的,其调整类型就为LR,其他类推。这一类型也可以从结点A,B的平衡因子符号看出,总之就是看哪边插入结点哪边沉。

3. 进行调整:

最简单的LL/RR型,分别是右旋/左旋调整,如下图,上文所说的B结点调整为根结点,而且该结点的另一子树(没新插结点的)连接到原根结点。

 

看懂了左旋和右旋后,LR型调整就要先 左旋 再 右旋,需要调整两次,先左旋是为了把LR型调整为LL型,这样就转变为我们解决过的问题了;同理, RL型调整要先 右旋 再 左旋,先右旋把RL型转为RR型。下面我们看博客中的一个例子。

插入的几种调整方法简要介绍到这,具体代码实现看后面的题目啦。

平衡二叉树结点删除

插入的四种类型让人头疼?删除操作才是更烦人的。

1. 找到待删结点,类似普通二叉搜索树的删除操作,若其有左右子树,需进行相应调整,保持BST性质;

2. 从删除结点或替换到此处的结点到根向上操作,修改各结点平衡因子;

3. 途中遇到某祖先结点A失衡(平衡因子为2/-2)则调整,删除结点在A的左子树,则看A的右孩子结点B的平衡因子情况,决定四种调整类型;

4. 继续这个过程,直到根结点,也就是说在平衡二叉树上删除一个结点可能引起多次调整。是不是有点崩溃?

平衡二叉树的查找

最后提一下查找算法,可以看到,我们为了维持平衡性,在对AVL插入和删除时,进行如此复杂的树高调整,平衡二叉树是一棵高度平衡的二叉树,所以查询的时间复杂度是 O(logN) 。


好啦, 我们消化一下这一节的基本知识,进入实战部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值