红黑树——动态+静态图
作者 | 陌无崖
转载请联系授权
目录
概念引入折半法二叉查找树AVL红黑树特点维持平衡变化规则变色左旋右旋示例动态旋转
概念引入
假如我们遇到一个猜数字的题,即给定一个序列,猜出该序列中的某个数字。一般该序列是有序的,用户猜出一个数字之后提示该数字是大了还是小了。
折半法
这种方法最容易想到,每次猜出该序列中的中位数,然后将序列分成了两个序列,这样每猜一次,将排除掉一般的数字。
缺点是必须保证序列有序
二叉查找树
使用这种方法我们可以将原始的数据存储到二叉查找树中,在二叉查找树中,任意结点的左子树的值都比该结点小,右子树的值都比该结点大。同样也可以快速定位到某个数字。但是有没有缺点?
当我们建立二叉树的时候,假如我们传入的序列如下:
9,8,7,6,5,4,3,2,1
上述序列构建的二叉树就成为了一个线性的链表,没有右子树。这样查找效率随数据的变化而降低。因此我们需要一种平衡的二叉树,即左右子树的高度相差不大。
AVL
由于二叉查找树的缺点,AVL树解决了上述问题,AVL是一种有着特殊条件的二叉树,即平衡二叉树。它的特点是所有结点的左右子树高度不超过1,由于该二叉树平衡度最高,因此查找的效率也很高,但是同样也带来了新的问题,插入数据和删除消耗时间,因此这种数据结构只能适合较少的插入和删除的应用场景。
红黑树
红黑树是在AVL的基础上进行改进,通过使每个结点有颜色来保证二叉树的平衡。如下图所示:
红黑树
特点
- 每个结点要么是红色,要么是黑色
- 每个红色结点的两个子节点都是黑色
- 根节点永远是黑色
维持平衡
当插入数据的时候,因为不知道该结点会插入到哪个地方,因此也不知道该结点应该是什么颜色。通常我们将结点置为红色,然后再去更正我们的二叉树,为什么还需要更正呢?因为当我们插入一个红色的结点的时候,有可能会打破红黑树的第二个特点,将会出现两个连续的红色的结点。比如上图中插入21:
插入数字21
通常我们有三种方法维持平衡,分别是变色,左旋,右旋。
变化规则
变色
特点
- 当前结点的父亲是红色&& 它的祖父结点是另一个子节点
- 叔叔结点也是红色
规则
- 把父亲变成黑色
- 把叔叔设为黑色
- 把祖父也就是父亲的父亲设置为红色
- 把指针定义到祖父结点,设置为当前要操作的结点
左旋
特点
- 当前的父亲结点时红色,叔叔是黑色的时候,且当前的结点时右子树。
规则
- 左旋以父节点作为左旋
右旋
特点
- 当前的父节点时红色,叔叔是黑色,且当前结点是左子树,右旋
规则
- 把父节点变成黑色
- 把祖父变成红色
- 以祖父结点进行旋转
示例
高清大图可以公众号后台回复红黑树
动态旋转
旋转
关于旋转源码可以进入我的github仓库查看,点击阅读原文进入我的github
- hdu----(2222)Keywords Search(ac自动机)
- MySQL数据库(四):约束条件
- hdu----(2084)数塔(dp)
- golang简单tls协议用法完整示例
- spark开发环境详细教程1:IntelliJ IDEA使用详细说明
- MySQL数据库(五):索引
- hdu----(1466)计算直线的交点数(dp)
- golang模板template自定义函数用法示例
- 程序员你为什么这么累【续】:编写简陋的接口调用框架 - 动态代理学习
- hdu---(Tell me the area)(几何/三角形面积以及圆面积的一些知识)
- MySQL数据库(六):体系结构和存储引擎
- hdu----(2222)Keywords Search(trie树)
- MySQL数据库(七):数据导出与导入
- flume与kafka整合高可靠教程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- win10使用WSL 2运行Docker Desktop,运行文件从C盘迁移到其他目录
- htaccess简介和16个小技巧
- Nginx禁止指定目录运行PHP脚本
- 机器学习数学笔记|偏度与峰度及其 python 实现
- ABP(ASP.NET Boilerplate Project)快速入门
- IdentityServer4 4.x版本 配置Scope的正确姿势
- 个人总结的部分数据验证规则
- 一句话实现php日期转中文汉字
- .Net Core微服务入门全纪录(八)——Docker Compose与容器网络
- PHP中少用但是很好用的方法
- 使用一维数据构造简单卷积神经网络
- .Net Core微服务入门全纪录(完结)——Ocelot与Swagger
- PHP使用GD库生成文件
- 反向传播算法从原理到实现
- 基于EntityFramework 6 Code First实现多租户的一种思路