二叉搜索树
所谓二叉搜索树(binary search tree),可提供对数时间的元素插入和访问。二叉搜索树的节点放置规则是:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。因此,从根节点一直往左走,即可得到最小元素;从根节点一直往右走,即得最大元素。
要在一颗二叉搜索树中找到最大和最下元素,是一件很简单的事。插入和删除元素比较复复杂。
插入元素时,可从根节点开始,遇到键值较大者就向左,遇到键值较小者就向右,一直到尾端,即为插入点。
欲删除节点A,情况可分为两种。如果A只有一个子节点,就将A的子节点连到A的父节点,并将A删除。如果A有两个子节点,就以右子树内的最小节点取代A。注意,右子树的最小节点极易获得:从右子节点开始(视为右子树的根节点),一直向左走至底即是。
平衡二叉树
平衡二叉树(Balanced Binary Tree)又被称为AVL树。具有以下性质。它是一棵空树或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。
平衡二叉树很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)
。但是频繁的旋转会使插入和删除牺牲掉O(logN)
左右的时间。 只要不满足上面的条件,就要通过旋转来保持平衡,因为旋转非常耗时,所以我们可以知道平衡二叉树适合于插入和删除次数比较少,但查找次数多的情况。
平衡二叉树由于维护这种高度平衡所付出的代价比从中获得效率收益还大。故而实际应用的不多。更多的应用还是追求局部平衡而不是非常严格的整体平衡的红黑树。
对于失衡情况,只要调整“插入点至根节点”路径上,平衡状态被破坏的各节点中最深的那一个,便可使整棵树重新获得平衡。
假设该最深节点为X,由于节点最多拥有两个子节点,而所谓“平衡被破坏”意味着X的左右两棵子树的高度相差2。因此可以分为4种情况。
- 插入点位于X的左子节点的左子树——左左
- 插入点位于X的左子节点的右子树——左右
- 插入点位于X的右子节点的左子树——右左
- 插入点位于X的右子节点的右子树——右右
情况1,4彼此对称,称为外侧插入。可以采用单旋转操作调整解决。情况2,3彼此对称,称为内测插入,可以采用双旋转操作调整解决。
红黑树
红黑树并不追求“完全平衡”,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。红黑树能够以O(logn)
的时间复杂度进行搜索,插入,删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。红黑树的算法时间复杂度和平衡二叉树相同,但统计性能比平衡二叉树更高。
红黑树RB-TREE,不仅是一个二叉搜索树,而且必须满足以下规则:
- 每个节点不是红色就是黑色
- 叶子节点都是黑色的
- 根节点为黑色
- 如果节点为红,其子节点必须为黑
- 任一节点至NULL(树尾端)的任何路径,所含的黑节点数必须相同。
根据规则4,新增节点必须为红色;根据规则3,新增节点的父节点必须为黑。当新节点根据二叉搜索树的规则到达其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。
插入节点
假设我为图5-13的RB-TREE分别插入3,8,35,75。如图所示
假设新节点为X,其父节点为P,祖父节点为G,伯父节点(父节点的兄弟节点)为S,曾祖父节点为GG。现在,根据二叉搜索树的规则,新节点X必为叶节点,根据红黑树规则4,X必为红色节点。若P为红色节点(这就违背了规则3,必须调整树形),则G必为黑,于是,根据X的插入位置及外围节点(S和GG)的颜色,有了以下四种考虑。
- 状况1:S为黑且X为外侧插入。对此情况,我们先对P,G做一次单旋转,并更改P,G颜色。
- 状况2:S为黑且X为内测插入,对此情况,我们必须先对P,X做一次单旋转并更改G,X颜色,再将结果对G做一次单旋转,即可再次满足红黑树规则3。
- 状况3:S为红且X为外侧插入。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果GG为黑,一切搞定。但如果GG为红,则问题就比较大些。
- 状况4:S为红且X为外侧插入。对此情况,先对P和G做一次单旋转,并改变X的颜色。如果此时GG也是红色节点,还得持续往上做,直到不再有父子连续为红的情况。
一个由上而下的程序
为了避免状况4“父子节点皆为红色”的情况持续向RB-TREE的上册结构发展,形成处理时效上的瓶颈。我们可以施行一个由上而下的程序:假设新增节点为A,那么就沿着A的路径,只要看到有某节点X的两个子节点皆为红色,就把X改为红色,并把两个子节点改为黑色。
如图所示:
但是如果X的父节点P亦为红色(注意,此时S绝不可能为红),就得像状况1一样地做一次单旋转并改变颜色,或是像状况2一样地做一次双旋转并改变颜色。