赞
踩
平衡二叉搜索树(Balanced Binary Search Tree),英文简称 BBST。经典常见的平衡二叉搜索树是 AVL 树和红黑树。
二叉搜索树(Binary Search Tree)是二叉树的一种,英文简称 BST。又称为二叉查找树、二叉排序树。
它的特点是任何一个结点的值都大于其左子树的所有结点的值,任何一个结点的值都小于其右子树的所有结点的值。
平衡(Balance):就是当结点数量固定时,左右子树的高度越接近,这棵二叉树越平衡(高度越低)。而最理想的平衡就是完全二叉树/满二叉树,高度最小的二叉树。
一棵二叉搜索树平均时间复杂度可以认为是树的高度 O(h)。像左边这棵,结点的左右子树的高度接近,属于一棵平衡二叉搜索树,O(h) = O(logn);而右边这棵,高度达到了最大,已经退化成了链表,O(h)=O(n)。
当二叉树退化成链表时,性能是很低的,所以我们需要在结点的插入、删除操作之后,想办法让二叉搜索树恢复平衡(减小树的高度)。但是如果为了追求最理想的平衡,而增加了时间复杂度也不是很有必要,因此比较合理的方案就是:用尽量少的调整次数达到适度平衡。
由此引申出 AVL 树的概念。
AVL 树是最早发明的自平衡二叉搜索树之一。
平衡因子(Balance Factor):某结点的左右子树的高度差。
每个叶子结点的平衡因子都是 0。看这棵二叉搜索树,红色数字标注了每个结点对应的平衡因子。
举例:
8 的左子树高度为 2,右子树高度为 1,因此它的平衡因子为 1;5 的左子树高度为 0,右子树高度为 3,因此它的平衡因子为 -3;4 的左子树高度为 2,右子树高度为 4,因此它的平衡因子为 -2;
再看这棵 AVL 树和它每个结点对应的平衡因子:
可以看到 AVL 树具有以下特点:
B 树(Balanced Tree)是一种平衡的多路搜索树,多用于文件系统、数据库的实现。这是一个简单的 3 阶 B 树:
m 阶 B 树指的是一个结点最多拥有 m 个子结点。假设一个结点存储的元素个数为 x,那么如果这个结点是:
如果有子结点,子结点个数为 y = x + 1,那么如果这个结点是:
向上取整(Ceiling),指的是取比自己大的最小整数,用数学符号 ┌ ┐ 表示。
向下取整(Floor),指的是取比自己小的最大整数,用数学符号 └ ┘ 表示。
比如 m = 3, 子结点个数 2 ≤ y ≤ 3,这个 B 树可以称为(2,3)树、2-3 树;
比如 m = 4, 子结点个数 2 ≤ y ≤ 4,这个 B 树可以称为(2,4)树、2-3-4 树;
比如 m = 5, 子结点个数 3 ≤ y ≤ 4,这个 B 树可以称为(3,5)树、3-4-5 树;
以此类推。
这是一棵二叉搜索树,通过某些父子结点合并,恰好能与上面的 B 树对应。我们可以得到结论:
红黑树是一种含有红黑结点并能自平衡的二叉搜索树。
为了保证平衡,红黑树必须满足以下性质:
根据上面的性质,可以画出这样一棵红黑树。接下来对红黑树做等价变换,即将所有的红色结点上升一层与它的父结点放在同一行,这就很像一棵 4 阶 B 树,转换效果如下图所示。
可以得出结论:
当我们对一棵平衡二叉搜索树进行插入、删除的时候,很可能会让这棵树变得失衡(最坏可能导致所有祖先结点失衡,但是父结点和非祖先结点都不可能失衡),为了达到平衡,需要对树进行旋转。而红黑树能够达到自平衡,靠的也就是左旋、右旋和变色。
旋转操作是局部的。当一侧子树的结点少了,向另一侧“借”一些结点;当一侧子树的结点多了,则“租”一些结点给另一侧。
为了更清楚地讲解这部分内容,先声明几个概念:
左旋指的是以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
不考虑结点颜色,可以看到左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树移动。
右旋指的是以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
不考虑结点颜色,可以看到右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树移动。
变色指的是结点的颜色由红变黑或由黑变红。
将左旋、右旋和变色结合起来,得到一套变换规则:
变色:如果当前结点的父结点和叔父结点是红色,那么:
左旋:当前结点是右子树,且父结点是红色,叔父结点是黑色,对它的父结点左旋
右旋:当前结点是左子树,且父结点是红色,叔父结点是黑色,那么:
由于红黑树本来就是平衡二叉搜索树,并且搜索也不会破坏树的平衡,所以搜索算法也与平衡二叉搜索树一致:
具体步骤:
红黑树插入操作分为下面两步:
具体步骤:
建议新添加的结点默认为红色,因此这样能够让红黑树的性质尽快满足。不过如果添加的结点是根结点,设为黑色即可。
总结一下红黑树插入可能出现的所有场景。
场景 1:红黑树为空树
红黑树的性质 2:根结点必须是黑色。
处理:直接把插入结点设成黑色并作为根结点。
场景 2:插入结点的 key 已存在
二叉搜索树中不能插入相同元素,既然结点的 key 已经存在,红黑树也已平衡,无需重复插入。
处理:
场景 3:插入结点的父结点为黑色
插入的结点默认是红色的,当它的父结点是黑色时,并不会破坏平衡。
处理:直接插入。
场景 4:插入结点的父结点为红色
如果插入结点的父结点为红色,那么父结点不可能为根结点,所以插入结点总是存在祖父结点。这点很重要,后续的旋转操作需要祖父结点的参与。
场景 4.1:存在叔父结点,且为红色
由红黑树性质 4 可知:红色结点不能连续。那么此时该插入子树的红黑层数的情况是:黑-红-红。显然最简单的处理方式就是将其改为:红-黑-红。
处理:
场景 4.2:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的左子结点
这种场景下,叔父结点所在的子树的黑色结点就比父结点所在子树的多,不满足红黑树的性质 5。
场景 4.2.1:插入结点是左子树
处理:
场景 4.2.2:插入结点是左子树
这种场景显然可以转换为 4.2.1。
处理:
场景 4.3:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的右子结点
相当于场景 4.2 的方向反转,直接看图。
场景 4.3.1:插入结点是左子树
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。