二叉查找树(Binary Search Tree)
二叉查找树又可以称之为 : 二叉搜索树 , 二叉排序树 , 它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势 , 下图中这棵树,就是一棵典型的二叉查找树
那么这样的数据结构有什么好处呢?
比如我们来查找一个元素 : 23
如果我们用链表来存储的话 : 查找元素23就需要一个一个遍历 , 直到找到匹配的元素
如果使用二叉查找树来存储的话 , 查找元素23 , 首先和根节点20比较 , 发现比20大 , 那么就查找树右边的元素 , 发现比30小 , 那么就继续查找30左边的元素 , 发现比25小 , 那么就查找25左边的元素 , 这样一来 , 整个元素查找所消耗的时间远远的比遍历整个链表快了许多 , 这就是二叉查找树的思想 , 查找所需的最大次数等于这个树的高度
在插入结点的时候也是使用类似的方法 , 找到元素的位置然后插入 , 虽然看起来挺方便的 , 但是也有它的缺陷 , 比如这种情况 : 原有的树结构为这样(如下图所示) , 现在需要插入9,8,7,6,5这几个元素
元素插入之后就变成这样了(如下图所示)
可以发现 , 这样的存储和链表没什么太大的区别 , 虽然符合二叉查找树的特性 ,但是查找的性能大打折扣
那么如何解决二叉查找树多次插入新结点导致的不平衡呢?
这个时候红黑树诞生了 , 红黑树(Red Black Tree) 是一种自平衡的二叉查找树 , 它除了符合二叉查找树的基本特征外还包括了以下特征 :
根节点必须是黑色的
节点是红色和黑色
每个叶子节点都是黑节点的空节点(NIL节点)
每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有连续的两个红色节点)
从任意节点到每个叶子的所有路径上的黑色节点数量相同
我们用下面的一张图来表示 :
这个图看起来比二叉查找树的图复杂一点 , 正是有了这些规则的限制 , 才保证了红黑树的自平衡 , 红黑树从根到叶子的最长路径不会超过最短路径的2倍
当进行插入和删除节点操作的时候 , 这个树的平衡可能被打破 , 所以就需要一些措施来保证这个树的平衡 , 那么在什么情况下会打破这棵树的平衡呢? 下面来看几种例子
情况一 : 插入一个元素23
插入一个元素28之后 , 由于父亲节点是黑色节点 , 并不会打破红黑树的规则 , 所以没有问题
情况二 : 插入一个元素33
为什么插入的这个节点是红色的?
因为上面提到红黑树的第五个特征就是 : 从任意节点到每个叶子的所有路径上的黑色节点数量相同 , 所以这块不能是黑节点 , 只能是红节点
由于父亲节点是红色节点 , 因此打破了红黑树的规则 : 每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有连续的两个红色节点) , 所以就必须做出调整 , 使它符合红黑树的规则
那么应该做出什么样的调整呢?
旋转(左旋右旋)变色
旋转和颜色变换规则:所有插入的点默认为红色
变色
变色的情况 : 当前节点的父节点是红色的 , 当前节点的叔叔节点是红色的
下面我们实际分析一下:
现在我们插入一个节点33 , 由于新插入的节点是红色的 , 打破了红黑树的平衡 , 所以要进行旋转变色来维持这个平衡
首先就是判断是旋转还是变色: 33的父节点是红色 , 叔叔节点也是红色 , 符合变色规则
先把父亲节点35变为黑色 , 再把叔叔节点45变为黑色 , 把爷爷节点40变为红色
由于40的父亲节点30和叔叔节点10也都是红色 , 所以要继续改变颜色 , 把父亲节点30变为黑色 , 把叔叔节点10变为黑色 , 爷爷节点20变为红色
由于爷爷节点是根节点 , 根节点只能为黑色 , 所以变色为黑色 , 这样 , 我们得到了一棵新的红黑树
左旋
当前父结点是红色,叔叔是黑色的时候,且当前的结点是右子树 , 以父节点为中心逆时针旋转,使得父节点被自己的右孩子取代,而自己成为自己的左孩子
插入一个元素41
这个时候红黑树的规则又被破坏了 , 继续进行判断 , 发现符合变色的条件 , 进行变色
变色完成之后 , 发现40和35又破坏了红黑树的规则 , 继续进行判断 , 40的父节点是红色 , 叔叔节点是黑色的 , 不符合变色的条件了 , 但是符合左旋转的条件 , 所以以父节点为中心逆时针旋转进行左旋
左旋之后 , 发现35和40节点还是不符合红黑树的规则 , 也不符合变色和左旋的规则 , 所以我们接着这个结果在下面分析右旋
右旋
当前节点的父节点是红色的 , 叔叔节点是黑色的 , 且当前节点是左子树 , 以自己的爷爷节点为中心顺时针旋转 , 使得父节点被自己的左孩子取代,而自己成为自己的右孩子
接着上面左旋的结果来分析 , 由于符合右旋的结果 , 所以我们围绕父节点40来进行顺时针旋转 , 旋转后结果如下
右旋之后40节点成为了根节点 , 由于根节点只能是黑色 , 所以进行变色
变色之后发现还是不满足红黑树的规则4 , 所以把45节点再进行变色
插入新节点41之后 , 经过一系列的旋转变色操作 , 最终得到了一棵红黑树
以上我们通过图文的方式简单的分析了红黑树的旋转变色 , 在实际的插入删除中 , 还涉及到很多的条件 , 这里就不一一列举了 , 主要体会红黑树自平衡调整的主体思想