前言
017年,小灰曾经发布过一篇关于红黑树的漫画,当时由于时间仓促,部分知识点一带而过,并没有讲解得很细致全面。
最近,小灰把这个知识点重新做了总结,分成上下两篇,希望大家把红黑树这个重要的数据结构彻底吃透。
![7fb4f4d2449e8aa6374fedd994bb83dc.png](https://i-blog.csdnimg.cn/blog_migrate/19c52b93eb2758f941d5e046a4f74429.jpeg)
![73bbafe6596c445a5d201175bad32dc3.png](https://i-blog.csdnimg.cn/blog_migrate/b16c3247b823fbb803a1ba3f3d431124.jpeg)
————— 第二天 —————
![403536f9e6f48eb2ff5bd6b3166c8b0f.png](https://i-blog.csdnimg.cn/blog_migrate/0277a494c266c8c4d26513fa085510c3.jpeg)
![2c45222025a08aea68424e6e57ebcf77.png](https://i-blog.csdnimg.cn/blog_migrate/1364e3048b69fe054c40a43930dc6b42.jpeg)
![0280d67158e1b79c95f355bc3b1f812a.png](https://i-blog.csdnimg.cn/blog_migrate/58fa12278e9b655bc700d47f3e018028.jpeg)
![c18f020f110e5a3e8855bef9ccaa93be.png](https://i-blog.csdnimg.cn/blog_migrate/32be05029d562e18437257c4da0fe8f1.jpeg)
![22b172ba57d009d290f4290a30a020f5.png](https://i-blog.csdnimg.cn/blog_migrate/8af9620b7c8f1971782a416c7ebb2853.jpeg)
![4251685959198c1fbd42093a81cdbe6d.png](https://i-blog.csdnimg.cn/blog_migrate/a8b75e0771b5c3df797616c87ddbed2b.jpeg)
![fe57a233b477f5b6ff243fe6c5272bda.png](https://i-blog.csdnimg.cn/blog_migrate/37eea748647cf0d1d6866a3622a59a84.jpeg)
————————————
![7102ed9d8a19086f4b408d19a94a241a.png](https://i-blog.csdnimg.cn/blog_migrate/91e2657c1e2de4ffa19392968af7e1a4.jpeg)
![29eac5418687ecac445f01857127c9e2.png](https://i-blog.csdnimg.cn/blog_migrate/9a3bf30437346de61948051e87c64049.jpeg)
![27f02dae9324a3829dbadabb37e11b6e.png](https://i-blog.csdnimg.cn/blog_migrate/d0549d3d1f51d86d84e3c164d64297b3.jpeg)
![87c63d9d63f145d34bdbd3244709c453.png](https://i-blog.csdnimg.cn/blog_migrate/452f4a80a140b57934b6e8613ea44942.jpeg)
二叉查找树(BST)具备什么特性呢?
1.左子树上所有结点的值均小于或等于它的根结点的值。
2.右子树上所有结点的值均大于或等于它的根结点的值。
3.左、右子树也分别为二叉排序树。
下图中这棵树,就是一颗典型的二叉查找树:
![a7d7845961da587077a5d708fc145b66.png](https://i-blog.csdnimg.cn/blog_migrate/68292485d68a44c9e4e70a95f6f6da8a.jpeg)
![033b7493b16dcf280c4c8363d120052a.png](https://i-blog.csdnimg.cn/blog_migrate/4986c4184d69223bfaad9207c7835e02.jpeg)
1.查看根结点9:
![309be69ab6522e3fca371753b65e9270.png](https://i-blog.csdnimg.cn/blog_migrate/1d43425d74c5d00742a64b99f53c01a7.jpeg)
2.根据二叉查找树左子树小、右子树大的特性,10 > 9,因此值为10的结点只可能在根结点的右子树当中,我们查看右孩子结点13:
![014f89ddb30f13e77f04a4dcac08936f.png](https://i-blog.csdnimg.cn/blog_migrate/129436b2698ff3237826589ad0b820c0.jpeg)
3.由于10 < 13,因此查看左孩子11:
![147a78efa74512670389ddea69cb34c7.png](https://i-blog.csdnimg.cn/blog_migrate/ee094b6fac7d9690de4f909d97a10268.jpeg)
4.由于10 < 11,因此查看左孩子10,发现10正是要查找的结点:
![2b9f57f7aabd111778219e2573b0045f.png](https://i-blog.csdnimg.cn/blog_migrate/29a9d152a55cdc53704f74380ec514ac.jpeg)
![d3787a58c7993b50bc1bb9b163b62dfe.png](https://i-blog.csdnimg.cn/blog_migrate/8807f6469a83de55fea17d19a46c1daf.jpeg)
![f2819739481f89dfc8a5b946f3c0aabb.png](https://i-blog.csdnimg.cn/blog_migrate/d5288cb799121aca0b615e92479e0d08.jpeg)
![f0b752157bb105df4ac49d54bf79ac13.png](https://i-blog.csdnimg.cn/blog_migrate/7354545282b8379b497d0d3c7f925d85.jpeg)
![0454a042cabdc9c20420d36024fe6c98.png](https://i-blog.csdnimg.cn/blog_migrate/347860d8304ceeeeb9ffbf98716d494b.jpeg)
![8782b14ae6086d10e9dd094b85deb0d7.png](https://i-blog.csdnimg.cn/blog_migrate/28b7b77374ad6bdb1c61d3ff57b89e65.jpeg)
![d99df8728bc7b90360732ae21cab5d11.png](https://i-blog.csdnimg.cn/blog_migrate/b3a0c2659d8b2148bb48a2a2da1b2489.jpeg)
![c2fa549c3d08a24c188ea9164eec250c.png](https://i-blog.csdnimg.cn/blog_migrate/706c387499e38d2e876627d00e781425.jpeg)
假设初始的二叉查找树只有三个结点,根结点值为9,左孩子值为8,右孩子值为12:
![62b5601fc8be6e2a59a9af24fb28ae79.png](https://i-blog.csdnimg.cn/blog_migrate/e595fc197b086a1d12d7b91aa1412b01.jpeg)
接下来我们依次插入如下五个结点:7,6,5,4,3。依照二叉查找树的特性,结果会变成什么样呢?
![9eb9921c9880ab1df2cbf3634f33358b.png](https://i-blog.csdnimg.cn/blog_migrate/cd80a61f7e42bd70d2ec3b14e444bcaa.jpeg)
![443cd2afa804ed8438f3dbf02df1816a.png](https://i-blog.csdnimg.cn/blog_migrate/72b2228e6570be01e5936f5b217f4a26.jpeg)
![3f0c1a89699ccc87995a70bd31cb4494.png](https://i-blog.csdnimg.cn/blog_migrate/288cdba3bf0820610d83b22d75cb7b76.jpeg)
![c2f585a1b5bf4787ce29b9fa4040d82e.png](https://i-blog.csdnimg.cn/blog_migrate/d56931e67f7c589d9f071d74795dfcd3.jpeg)
![ce59516f3003a6105964b58f909b6389.png](https://i-blog.csdnimg.cn/blog_migrate/704898ae262ccf5c37e1bfdd57cc9122.jpeg)
1.结点是红色或黑色。
2.根结点是黑色。
3.每个叶子结点都是黑色的空结点(NIL结点)。
4 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。
下图中这棵树,就是一颗典型的红黑树:
![8a432dccc6e9e0bf202edff5f0f35ac3.png](https://i-blog.csdnimg.cn/blog_migrate/acce5ae3a4013bb781911c5b9dff20a3.jpeg)
![0c09c5a1821b80723da553ee938ed350.png](https://i-blog.csdnimg.cn/blog_migrate/97602647e8e52cafa55f4dd24ad23d98.jpeg)
![b3214de4f3ed7279c29212647866dab9.png](https://i-blog.csdnimg.cn/blog_migrate/9d53e5e81a73c2895d1564b9987e298f.jpeg)
![efdbf21c848f2024b54a1b051dae6eeb.png](https://i-blog.csdnimg.cn/blog_migrate/ae97108940336e5dc621c573cbb0fbdb.jpeg)
什么情况下会破坏红黑树的规则,什么情况下不会破坏规则呢?我们举两个简单的例子:
1.向原红黑树插入值为14的新结点:
![dff6a56eeef86bd09e2ec05d91e50f77.png](https://i-blog.csdnimg.cn/blog_migrate/bc42774fa1af6a90dd55dc38ddae5588.jpeg)
由于父结点15是黑色结点,因此这种情况并不会破坏红黑树的规则,无需做任何调整。
2.向原红黑树插入值为21的新结点:
![9534e89cf9c88ff3035513a480714527.png](https://i-blog.csdnimg.cn/blog_migrate/a021d78e725d20ec1eb137ad00c29a53.jpeg)
由于父结点22是红色结点,因此这种情况打破了红黑树的规则4(每个红色结点的两个子结点都是黑色),必须进行调整,使之重新符合红黑树的规则。
![643e2dcf80c6979eb34f7ca01bbda77b.png](https://i-blog.csdnimg.cn/blog_migrate/0dea3c43eee58ffed84a7822abd7486c.jpeg)
![36795e4c23b4b679d0b3ec45321e7531.png](https://i-blog.csdnimg.cn/blog_migrate/bf78ad88ad9875164d6dde27d2317b6c.jpeg)
变色:
为了重新符合红黑树的规则,尝试把红色结点变为黑色,或者把黑色结点变为红色。
下图所表示的是红黑树的一部分(子树),新插入的结点Y是红色结点,它的父亲结点X也是红色的,不符合规则4,因此我们可以把结点X从红色变成黑色:
![5e5dbb73863ae24281cd2f2e76234e2a.png](https://i-blog.csdnimg.cn/blog_migrate/d5529c6a08f677e61829e8af43231bcf.jpeg)
但是,仅仅把一个结点变色,会导致相关路径凭空多出一个黑色结点,这样就打破了规则5。因此,我们需要对其他结点做进一步的调整,后文会详细说明。
左旋转:
逆时针旋转红黑树的两个结点,使得父结点被自己的右孩子取代,而自己成为自己的左孩子。说起来很怪异,大家看下图:
![c838ca78e823542ee2b43e9830edfacc.png](https://i-blog.csdnimg.cn/blog_migrate/a99ecb6a309269a47cdddb9c41b0bd17.jpeg)
图中,身为右孩子的Y取代了X的位置,而X变成了自己的左孩子。此为左旋转。
右旋转:
顺时针旋转红黑树的两个结点,使得父结点被自己的左孩子取代,而自己成为自己的右孩子。大家看下图:
![0f36133a8b776b34cd2b1df1b9153f25.png](https://i-blog.csdnimg.cn/blog_migrate/33125dda64fc9512ab67afd6c2228d06.jpeg)
图中,身为左孩子的Y取代了X的位置,而X变成了自己的右孩子。此为右旋转。
![47070609c38935839fd5066bb29740b6.png](https://i-blog.csdnimg.cn/blog_migrate/33b9ed569210f1525fae8bf029da7f13.jpeg)
![c0d04185bf07786fdcf13b19d3606f4b.png](https://i-blog.csdnimg.cn/blog_migrate/3c39af527a9a36b7a7f2764a8b08ecb9.jpeg)
![05c53e569f8836aedabfa8a70b5debbf.png](https://i-blog.csdnimg.cn/blog_migrate/90482a83c655ab12148068b567235378.jpeg)
局面1:新结点(A)位于树根,没有父结点。
![383f30a36a44002eae85a50a77ac74c4.png](https://i-blog.csdnimg.cn/blog_migrate/8fb6b3314de9ee0822ccd29439a04b07.jpeg)
(空心三角形代表结点下面的子树)
这种局面,直接让新结点变色为黑色,规则2得到满足。同时,黑色的根结点使得每条路径上的黑色结点数目都增加了1,所以并没有打破规则5。
![df353c8b662e42785884beff69f7d205.png](https://i-blog.csdnimg.cn/blog_migrate/99edca92a9d644a07ceaa27d4b8f6e7a.jpeg)
局面2:新结点(B)的父结点是黑色。
这种局面,新插入的红色结点B并没有打破红黑树的规则,所以不需要做任何调整。
![823bbb0361c23e32bf8c423eaa2877c0.png](https://i-blog.csdnimg.cn/blog_migrate/42c50d15dcc1f5a9ba9ab0ed21c1f4ba.jpeg)
局面3:新结点(D)的父结点和叔叔结点都是红色。
![4d1737658b1ae3bdaea573c0775a9c28.png](https://i-blog.csdnimg.cn/blog_migrate/0844275f26af92d066b6a31fa6565a1e.jpeg)
这种局面,两个红色结点B和D连续,违反了规则4。因此我们先让结点B变为黑色:
![d1ab893bebef4eb17b8558623f641ab8.png](https://i-blog.csdnimg.cn/blog_migrate/3cdb5755ab89abbfef8dcbafb33f00f6.jpeg)
这样一来,结点B所在路径凭空多了一个黑色结点,打破了规则5。因此我们让结点A变为红色:
![01cc387da19e164027d615f1426b2d3c.png](https://i-blog.csdnimg.cn/blog_migrate/865ecb49ff2a1de61ef7142498138fde.jpeg)
这时候,结点A和C又成为了连续的红色结点,我们再让结点C变为黑色:
![0226ad216bb07803e88c3fcfd4680fb5.png](https://i-blog.csdnimg.cn/blog_migrate/309cad49d738ab7dda1ba402a1c643f9.jpeg)
经过上面的调整,这一局部重新符合了红黑树的规则。
局面4:新结点(D)的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右孩子,父结点(B)是祖父结点的左孩子。
![6c4ff419d55678fcfc73d517e508b6e6.png](https://i-blog.csdnimg.cn/blog_migrate/7cc2ffc8d3aeaf402cd341e082b5651a.jpeg)
我们以结点B为轴,做一次左旋转,使得新结点D成为父结点,原来的父结点B成为D的左孩子:
![b7238ef9b4cec781761d650c2dd500d5.png](https://i-blog.csdnimg.cn/blog_migrate/a56b12c3d0138c17d4310fe3182f52bf.jpeg)
这样一来,进入了局面5。
局面5:新结点(D)的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的左孩子,父结点(B)是祖父结点的左孩子。
![7f8ed6a5144aab14cc879edca6a695a5.png](https://i-blog.csdnimg.cn/blog_migrate/0800ad4abc09dbf664f89f28f6bc5031.jpeg)
我们以结点A为轴,做一次右旋转,使得结点B成为祖父结点,结点A成为结点B的右孩子:
![aa734eb40112edd87dbfd4e2cbed9e34.png](https://i-blog.csdnimg.cn/blog_migrate/117c0efebcd32556c64a0c0a938d430e.jpeg)
接下来,我们让结点B变为黑色,结点A变为红色:
![4962384ef58dffe855a762b5e171e8f7.png](https://i-blog.csdnimg.cn/blog_migrate/14a44aa078f8104bf5184cfa7d0cf0a9.jpeg)
经过上面的调整,这一局部重新符合了红黑树的规则。
以上就是红黑树插入操作所涉及的5种局面。
或许有人会问,如果局面4和局面5当中的父结点B是祖父结点A的右孩子该怎么办呢?
很简单,如果局面4中的父结点B是右孩子,则成为了局面5的镜像,原本的右旋操作改为左旋;如果局面5中的父结点B是右孩子,则成为了局面4的镜像,原本的左旋操作改为右旋。
![175ca0d318ec092b9685ea121a0f1714.png](https://i-blog.csdnimg.cn/blog_migrate/8f9bb5dfaf2065416ac706833da6df85.jpeg)
![2c3bf57a004be7c128c13ecb588bfecb.png](https://i-blog.csdnimg.cn/blog_migrate/acfa23a697ce40bf0fd41f0bf4e7841f.jpeg)
给定下面这颗红黑树,新插入的结点是21:
![d7ec55af9144f13acda34be2f8574980.png](https://i-blog.csdnimg.cn/blog_migrate/f81455dfed58b189cfd6e65bbd604b72.jpeg)
显然,新结点21和它的父结点22是连续的红色结点,违背了规则4,我们应该如何调整呢?
让我们回顾一下刚才讲的5种局面,当前的情况符合局面3:
“新结点的父结点和叔叔结点都是红色。”
于是我们经过三次变色,22变为黑色,25变为红色,27变为黑色:
![40f3d975d4e9bd7c3b4e24f6c80dcd02.png](https://i-blog.csdnimg.cn/blog_migrate/038df6432ceac8f7053953baa06bc2cf.jpeg)
经过上面的调整,以结点25为根的子树符合了红黑树规则,但结点25和结点17成为了连续的红色结点,违背规则4。
于是,我们把结点25看做一个新结点,正好符合局面5的镜像:
“新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右孩子,父结点是祖父结点的右孩子”
于是我们以根结点13为轴进行左旋转,使得结点17成为了新的根结点:
![e6c1044bdaef794d79a180ec65ef0a3d.png](https://i-blog.csdnimg.cn/blog_migrate/5ada8798b8e8704de45acf2944489950.jpeg)
接下来,让结点17变为黑色,结点13变为红色:
![bf3a12fd9872f1430ab6c5a30d3e25f0.png](https://i-blog.csdnimg.cn/blog_migrate/462c9364d3c4fd6a93166a91956bec39.jpeg)
如此一来,我们的红黑树变得重新符合规则。
![654716c4e1084282294aed52fd54d0d9.png](https://i-blog.csdnimg.cn/blog_migrate/a1ce41df3b28cfd4b1ac793c19902108.jpeg)
![74fe156fe9e0793604b4a791ae09dbf3.png](https://i-blog.csdnimg.cn/blog_migrate/56b31a5d162fe8e6149da432c53ff1f9.jpeg)
![6268ab6c34fc157cdcb6ee5de8667ffc.png](https://i-blog.csdnimg.cn/blog_migrate/9693913abc49176e5f5bdc6e1f5cfdc5.jpeg)
作为一名程序员我们必须不断学习技术知识,更新自己的知识储备
对于一名程序员来说,技术知识方面是非常重要的,可以说是重中之重。要面试大厂,自己的知识储备一定要非常丰富,若缺胳膊少腿,别说在实际工作当中,光是面试这一关就过不了。对于技术方面,首先基础知识一定要扎实,包括自己方向的语言基础、计算机基础、算法以及编程等等。
结合自身的一个学习经历,总结了一套非常系统的复习包,包括思维脑图、Android基础知识、JAVA知识点汇总、Android扩展知识点、Android开源库源码分析、设计模式汇总、Gradle知识点汇总、常见面试算法题汇总等等。
01Android基础知识:
笔记里的知识点非常齐全,囊括了Activity、数据储存、屏幕适配、消息机制、线程异步、webview、进程、ipc、数据储存等大量知识点,每一个知识点都有非常详细的解析,这本万能宝典在手,不信还有搞不懂的面试题!
![6b0c950c1f7a33fec07bb013a2fdf0a9.png](https://i-blog.csdnimg.cn/blog_migrate/7472b7e8038ca2b00e8aafd2044a4577.jpeg)
01JAVA知识点汇总:
笔记里的知识点非常齐全,囊括了JVM、static、并发、Java反射、Spring原理、微服务、异常处理、数据库、数据结构等大量知识点,每一个知识点都有非常详细的解析,这本万能宝典在手,不信还有搞不懂的面试题!
![f342fb6ef57ff0a54b46ef39a8189d68.png](https://i-blog.csdnimg.cn/blog_migrate/e1c7a43a6593b473257411cd2986a717.jpeg)
02 手撕架构技术篇
该篇内容囊括了以下专题的高频面试题、实战文档以及使用总结。
![415008e022ed0b1b612554d7b73fb275.png](https://i-blog.csdnimg.cn/blog_migrate/7375800b133c85b810f61880551ef64a.jpeg)
03 最新大厂面试专题
这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等
![acabec4d9df8dc4844c5577ad5991229.png](https://i-blog.csdnimg.cn/blog_migrate/96feb1825534b2feabe3e41af4433929.jpeg)
04 实战电子书
关于实战,我想每一个做开发的都有话要说,对于小白而言,缺乏实战经验是通病,那么除了在实际工作过程当中,我们如何去更了解实战方面的内容呢?实际上,我们很有必要去看一些实战相关的电子书。目前,我手头上整理到的电子书还算比较全面,HTTP、自定义view、c++、MVP、Android源码设计模式、Android开发艺术探索、Java并发编程的艺术、Android基于Glide的二次封装、Android内存优化——常见内存泄露及优化方案、.Java编程思想 (第4版)等高级技术都囊括其中。
![65e0cb104c3de074e6b7cc1e8637a379.png](https://i-blog.csdnimg.cn/blog_migrate/7b276389e312a640886779c9699d8c8c.jpeg)
05Android小白到Android工程师的系统学习视频
关于视频这块,我也是自己搜集了一些,都按照Android学习路线做了一个分类。按照Android学习路线一共有八个模块,其中视频都有对应,就是为了帮助大家系统的学习。接下来看一下导图和对应系统视频吧!!!
- Android高级工程师进阶思维导图
![3c64c1a27c19ca6d809bed923b01a26a.png](https://i-blog.csdnimg.cn/blog_migrate/bd9cd71dfcc35380d605774d56170db2.jpeg)
- 对应导图的Android高级工程师进阶系统学习视频
![d382e0234bca4f9b2391354c2403c46b.png](https://i-blog.csdnimg.cn/blog_migrate/89347d3f116632e2bac586e3c92ec0d2.jpeg)
私信我【学习】加入我们的圈子领取资料和我们一起吧学习交流吧!