平衡AVL树
参考:
- 《C++数据结构与程序设计》R.L.K和A.J.R著,钱丽萍译,清华大学出版社
- 《2019年数据结构考研复习指导》王道论坛·组编,电子工业出版社
- 博客园:二叉搜索树的深度与搜索效率
- 博客园:AVL树学习(平衡二叉树)
- github:教你透彻了解红黑树
文章目录
发现自己有很多东西又忘了,所以一直在恶补。
1. 查找效率和二叉排序树的形态
二叉排序树查找算法的平均查找长度,主要取决于树的高度,即与二叉树的形态有关。
——《2019年数据结构考研复习指导》156页
(1)最坏情况
- 下图来自二叉搜索树的深度与搜索效率
- 该图的查找成功的平均查找长度为ASL=(1+2+3+4+5+6+7+8)/8=4.5
- 因为每一行只有一个点,查找根结点8成功的长度为1,因为所在的深度是1,然后每一行只有一个结点,所以查找成功长度逐渐加1,所以有了上述结果。
如果二叉排序树是一个只有右(左)孩子的单支树(类似于有序的单链表),其平均查找长度和单链表相同,为O(n)。
——《2019年数据结构考研复习指导》156页
- 当二叉排序树只有单支的时候,它查找时走的每一步长度就像是在查单链表一样,也是查找第一个点成功的长度为1,第二个点成功的长度为2,所以就是O(n)。
(2)最好情况
- 下图来自二叉搜索树的深度与搜索效率
- 该图的查找成功的平均查找长度为ASL=(1+2x2+3x4+4x1)/8=2.625
- 查找根结点5成功的长度为1,因为所在的深度是1,然后深度为2的层次里,有两个结点3和7,所以这两个点查找成功的长度为2x2,然后以此类推,就得出了上述的ASL。
如果二叉排序树的左、右字数的高度之差的绝对值不超过1,这样的二叉排序树成为平衡二叉树。它的平均查找长度达到 O ( log n ) O(\log n) O(logn)。
——《2019年数据结构考研复习指导》156页
- 当二叉排序树是平衡二叉树时,根节点的成功查找长度是1,刚好是 log 2 1 + 1 = 1 \log_{2} 1+1=1 log21+1=1。第2个结点3的成功查找长度是 log 2 2 + 1 = 2 \log_{2} 2+1=2 log22+1=2,第3个结点7的成功查找长度是 ⌊ log 2 3 ⌋ + 1 = 2 \left \lfloor\log_{2} 3\right \rfloor+1=2 ⌊log23⌋+1=2,以此类推,第n个点就是 ⌊ log 2 n ⌋ + 1 \left \lfloor\log_{2} n\right \rfloor+1 ⌊log2n⌋+1,所以就是 O ( log n ) O(\log n) O(logn).
2. AVL树
(1)AVL树的定义
AVL树是一棵二叉查找树,它的根的左子树和右子树的高度差至多为1,而且左右子树也是AVL树。
AVL树的每个结点都联系着一个平衡因子,根据左子树的高度大于、等于或小于右子树的高度,平衡因子可以为左-高、等-高或右-高(left-higher,equal-height,right-higher)。
——《C++数据结构与程序设计》393页
(2)平衡因子的定义
定义结点左子树与右子树的高度差为该结点的平衡因子,则平衡二叉树结点的平衡因子的值只可能是-1(右-高)、0(等-高)、1(左-高)。
——《2019年数据结构考研复习指导》156页
AVL树的延伸定义:
平衡二叉树可定义为它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度差的绝对值不超过1。
——《2019年数据结构考研复习指导》156页
- 下图来自AVL树学习(平衡二叉树)
- 左边的平衡二叉树,根结点5的左子树高度为4,右子树高度为3,左-高,所以平衡因子是1。对于结点2,将它看成一个新的根结点,那么它的左子树高度为2,右子树高度为3,右-高,所以平衡因子是-1。对于结点0,因为它既没有左子树,也没有右子树,等-高,所以平衡因子是0。
(3)旋转
① LL平衡旋转(右单旋转)
A结点的左孩子的左子树插入了新结点,所以将A右旋转,让B成为新的根结点,A结点重新连接子树BR。
② RR平衡旋转(左单旋转)
A结点的右孩子的右子树插入了新结点,所以将A旋转,让B成为新的根结点,A结点重新连接子树BL。
③ LR平衡旋转(先左后右双旋转)
A结点的左孩子的右子树插入了新结点,先对A的左子树B操作,对于左子树B,将C结点作为新的根结点,B结点重新连接C的左子树。然后将A结点变动,使C结点变成新的根结点,并将c的右子树与A结点连接。
④ RL平衡旋转(先右后左双旋转)
A结点的右孩子的左子树插入了新结点,先对A的右子树操作,对于根结点B,将C结点变为新的根结点,将C结点的右子树与B结点相连。然后将A结点变动,使C结点变成新的根结点,将C结点的左子树与A结点相连。
3. 堆排序
(1)特点
在排序过程中,将L[1…n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。
——《2019年数据结构考研复习指导》301页
完全二叉树也是AVL树的一种特殊状态。
(2)大根堆和小根堆
堆的定义如下:n个关键字序列L[1…n]称为堆,当且仅当该序列满足:
① L(i) ≤ L(2i) 且 L(i) ≤ L(2i+1) 或 ② L(i) ≥ L(2i) 且 L(i) ≥ L(2i+1) (1 ≤ i ≤ ⌊ n / 2 ⌋ \left \lfloor n/2 \right \rfloor ⌊n/2⌋)
满足第①中情况的堆称为小根堆(小顶堆),满足第②种情况的堆称为大根堆(大顶堆)。显然,在大根堆中,最大元素存放在根结点中,且对其任一非根结点,它的值小于或等于其双亲结点值。小根堆的定义刚好相反,根结点是最小元素。堆经常被用来实现优先级队列,优先级队列在操作系统的作业调度和其他领域有广泛的应用。
——《2019年数据结构考研复习指导》301页
如果是大根堆,它的每一棵左、右子树的根结点都是该子树中的最大值。如果是小根堆,它的每一棵左、右子树的根结点都是该子树中的最小值。
(3)构造小根堆和删除元素
题目来自《2019年数据结构考研复习指导》304页第10题
对关键序列{23, 17, 72, 60, 25, 8, 68, 71, 52}进行堆排序,输出两个最小关键码后的剩余堆是(D)。
A. {23, 72, 60, 25, 68, 71, 52}
B. {23, 25, 52, 60, 71, 72, 68}
C. {71, 25, 23, 52, 60, 72, 68}
D. {23, 25, 68, 52, 60, 72, 71}
- 将关键字序列按顺序一层一层地写成一棵树
- 因为最后要删除两个最小的关键字,所以要构造小根堆。
- 不管是构造大根堆还是构造小根堆,都一定是向上调整,一定是从最后左或者左右两个叶子结点开始往上调整。
- 因为52比71小,且52小于60,所以60和52互换
- 因为8比68小,且8小于72,所以72和8互换
- 因为8比17小,且8小于23,所以8和23互换
- 8是第一个最小的关键码,要删除
- 删除就是要把根结点元素和最后一个叶结点元素互换,然后把最后一个叶结点删除,将堆进行向下调整,就是从根结点开始往下调整。
- 因为17比23小,且17小于60,所以17和60互换
- 因为25比52小,且25小于60,所以25和60互换
- 17是第二个最小的关键码,按照刚才删除8那样对堆操作
- 将17和最后一个叶节点71替换,然后删除17
- 向下调整
- 因为23比25小,且23小于27,所以23和27互换
- 因为68比72小,且68小于71,所以68和71互换
- 按顺序:23,25,68,52,60,72,71