数据结构之二叉查找树(Binary Search Tree)和红黑树(Red Black Tree)

简介: 二叉查找树又可以称之为 : 二叉搜索树 , 二叉排序树 , 它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势 , 下图中这棵树,就是一棵典型的二叉查找树

二叉查找树(Binary Search Tree)

二叉查找树又可以称之为 : 二叉搜索树 , 二叉排序树 , 它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势 , 下图中这棵树,就是一棵典型的二叉查找树


a0c40e12c2002365f67b8a33f0cccdd1_f94dc3fdb6ca098db8b991dbc1f36a0b.png


那么这样的数据结构有什么好处呢?

比如我们来查找一个元素 : 23


如果我们用链表来存储的话 : 查找元素23就需要一个一个遍历 , 直到找到匹配的元素


f6979475a37b5a73da2bca0ee4cbc5a7_f9b7e1ac0a2c0871398238d2426385a2.png


如果使用二叉查找树来存储的话 , 查找元素23 , 首先和根节点20比较 , 发现比20大 , 那么就查找树右边的元素 , 发现比30小 , 那么就继续查找30左边的元素 , 发现比25小 , 那么就查找25左边的元素 , 这样一来 , 整个元素查找所消耗的时间远远的比遍历整个链表快了许多 , 这就是二叉查找树的思想 , 查找所需的最大次数等于这个树的高度


在插入结点的时候也是使用类似的方法 , 找到元素的位置然后插入 , 虽然看起来挺方便的 , 但是也有它的缺陷 , 比如这种情况 : 原有的树结构为这样(如下图所示) , 现在需要插入9,8,7,6,5这几个元素


fe403f57b6fd42cf9f221763235f1f1a_ab61fd03426c370ea782e4033e48c767.png


元素插入之后就变成这样了(如下图所示)


2c8b3eef420a9653b8501c7d3302eeb8_d1a5691e6a28b507160699af7b000eb0.png


可以发现 , 这样的存储和链表没什么太大的区别 , 虽然符合二叉查找树的特性 ,但是查找的性能大打折扣


那么如何解决二叉查找树多次插入新结点导致的不平衡呢?

这个时候红黑树诞生了 , 红黑树(Red Black Tree) 是一种自平衡的二叉查找树 , 它除了符合二叉查找树的基本特征外还包括了以下特征 :


根节点必须是黑色的


节点是红色和黑色


每个叶子节点都是黑节点的空节点(NIL节点)


每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有连续的两个红色节点)


从任意节点到每个叶子的所有路径上的黑色节点数量相同


我们用下面的一张图来表示 :


c6495e3688b43f9d88f901a3a1d929b2_b32e706341e2502edbaaae5549adbfa4.png


这个图看起来比二叉查找树的图复杂一点 , 正是有了这些规则的限制 , 才保证了红黑树的自平衡 , 红黑树从根到叶子的最长路径不会超过最短路径的2倍


当进行插入和删除节点操作的时候 , 这个树的平衡可能被打破 , 所以就需要一些措施来保证这个树的平衡 , 那么在什么情况下会打破这棵树的平衡呢? 下面来看几种例子


情况一 : 插入一个元素23

插入一个元素28之后 , 由于父亲节点是黑色节点 , 并不会打破红黑树的规则 , 所以没有问题


54c3ccdfa7e42963ceb939cac7c2f3c7_c4a8d0593bd4350bf5aef59bff995eb3.png


情况二 : 插入一个元素33


dd540b65edf2480708b6840e5ddb841c_cab12253e0d135dc806e8ab4df6d378a.png

为什么插入的这个节点是红色的?

因为上面提到红黑树的第五个特征就是 : 从任意节点到每个叶子的所有路径上的黑色节点数量相同 , 所以这块不能是黑节点 , 只能是红节点


由于父亲节点是红色节点 , 因此打破了红黑树的规则 : 每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有连续的两个红色节点) , 所以就必须做出调整 , 使它符合红黑树的规则


那么应该做出什么样的调整呢?

旋转(左旋右旋)变色


旋转和颜色变换规则:所有插入的点默认为红色

变色

变色的情况 : 当前节点的父节点是红色的 , 当前节点的叔叔节点是红色的


下面我们实际分析一下:


dd540b65edf2480708b6840e5ddb841c_cab12253e0d135dc806e8ab4df6d378a.png


现在我们插入一个节点33 , 由于新插入的节点是红色的 , 打破了红黑树的平衡 , 所以要进行旋转变色来维持这个平衡


首先就是判断是旋转还是变色: 33的父节点是红色 , 叔叔节点也是红色 , 符合变色规则


先把父亲节点35变为黑色 , 再把叔叔节点45变为黑色 , 把爷爷节点40变为红色



435a9dffde49fbefdb82c305c4bfc2b2_a1bcd3d5390c5c9baffbf5c2bf787d46.png

由于40的父亲节点30和叔叔节点10也都是红色 , 所以要继续改变颜色 , 把父亲节点30变为黑色 , 把叔叔节点10变为黑色 , 爷爷节点20变为红色


459b2f1e06dcd3218e746fa788c434c3_c9cd20b11f4ccdafcd1502bf283fab45.png


由于爷爷节点是根节点 , 根节点只能为黑色 , 所以变色为黑色 , 这样 , 我们得到了一棵新的红黑树


cbcaefd80f224d1680bfff80591cdeef_f51239263529ce394df0614ef743f64d.png


左旋

当前父结点是红色,叔叔是黑色的时候,且当前的结点是右子树 , 以父节点为中心逆时针旋转,使得父节点被自己的右孩子取代,而自己成为自己的左孩子


b630fb6a1f32e425e051765c989dc389_6a7265c1eabd093874e9437a5e19c77c.png


插入一个元素41


021f09962ee658acfe9f6c30e4d23db8_9eaecd88ea844ec98dcaeb37f7940aa5.png


这个时候红黑树的规则又被破坏了 , 继续进行判断 , 发现符合变色的条件 , 进行变色


91252f76479387574a539e6eeda15aa6_7c905618b88c1e414183b17be04b7609.png


变色完成之后 , 发现40和35又破坏了红黑树的规则 , 继续进行判断 , 40的父节点是红色 , 叔叔节点是黑色的 , 不符合变色的条件了 , 但是符合左旋转的条件 , 所以以父节点为中心逆时针旋转进行左旋


5bb3d5c1b1844f0675c27459b9ee66b2_a6bde9c941fd2726d44a0552dc91cb84.jpeg


左旋之后 , 发现35和40节点还是不符合红黑树的规则 , 也不符合变色和左旋的规则 , 所以我们接着这个结果在下面分析右旋


右旋

当前节点的父节点是红色的 , 叔叔节点是黑色的 , 且当前节点是左子树 , 以自己的爷爷节点为中心顺时针旋转 , 使得父节点被自己的左孩子取代,而自己成为自己的右孩子


接着上面左旋的结果来分析 , 由于符合右旋的结果 , 所以我们围绕父节点40来进行顺时针旋转 , 旋转后结果如下


48461840161c7107467ea310dad29296_8eb52fc73a7ec1b2556ff547dbc42a05.png


右旋之后40节点成为了根节点 , 由于根节点只能是黑色 , 所以进行变色


41125b2948d36a95832810cf17fafe17_81f28cd4034e9a96a92172d40cf6a5f2.png


变色之后发现还是不满足红黑树的规则4 , 所以把45节点再进行变色


656e68068dba31c1f95250186c47a80d_8f0ee42bf107a389ad71c63b43387963.png


插入新节点41之后 , 经过一系列的旋转变色操作 , 最终得到了一棵红黑树


以上我们通过图文的方式简单的分析了红黑树的旋转变色 , 在实际的插入删除中 , 还涉及到很多的条件 , 这里就不一一列举了 , 主要体会红黑树自平衡调整的主体思想

相关文章
|
2天前
|
机器学习/深度学习
数据结构-----树的易错点
数据结构-----树的易错点
16 4
|
2天前
|
存储 算法
实验 2:树形数据结构的实现与应用
实验 2:树形数据结构的实现与应用
6 0
|
2天前
|
存储 算法 Java
数据结构/C++:红黑树
数据结构/C++:红黑树
15 3
|
2天前
|
存储 算法 C++
数据结构/C++:AVL树
数据结构/C++:AVL树
10 2
|
2天前
|
JSON 数据可视化 Shell
数据结构可视化 Graphviz在Python中的使用 [树的可视化]
数据结构可视化 Graphviz在Python中的使用 [树的可视化]
11 0
|
2天前
|
存储 缓存 算法
数据结构与算法 树(B树,B+树,红黑树待完善)
数据结构与算法 树(B树,B+树,红黑树待完善)
14 0
|
2天前
数据结构===红黑树
红黑树是平衡二叉搜索树,关键点在于其满足5个性质,包括根节点为黑,叶子为黑,红色节点不能相邻且路径上黑节点数相等。插入和删除时结合左旋、右旋操作。插入时,针对叔叔节点颜色(红或黑),参照AVL树的失衡处理,分为4种情况,并调整颜色策略。删除操作同样复杂,涉及节点替换和颜色调整。
13 1
|
2天前
|
机器学习/深度学习 算法 测试技术
【单调栈】3113. 边界元素是最大值的子数组数目
【单调栈】3113. 边界元素是最大值的子数组数目
|
2天前
|
存储 NoSQL C语言
数据结构——顺序栈与链式栈的实现-2
数据结构——顺序栈与链式栈的实现
数据结构——顺序栈与链式栈的实现-2
|
2天前
|
存储 C语言
数据结构——顺序栈与链式栈的实现-1
数据结构——顺序栈与链式栈的实现
数据结构——顺序栈与链式栈的实现-1