文章目录

  • 1.2 红黑树
    • 1.2.1 2-3查找树
    • 1.2.2 红黑树
      • 结点定义
      • 旋转操作
      • 插入
      • put()实现
      • 红黑树的性质

1.2 红黑树

1.2.1 2-3查找树

因为二叉查找树是不平衡的,如果我们顺序插入十个数据,那么其树高为10。最坏情况则需要10次查找才能找到所需要的数据,那么我们如果将其构造成一个平衡树的话,我们就可以将其树高降低为lgN这个级别,这将会大大降低最坏情况下的查找时间。而2-3查找树就是将二叉查找树变为平衡树的数据结构。

首先我们引入一个概念:

  • 2- 结点:含有一个父键和两条链接,左链接指向的子键都小于父键,右链接指向的子键都大于父键的值
  • 3- 结点:含有两个父键和三条链接,左链接指向小于两个父键的子健,中间的链接指向大于最小父键且小于最大父键的子键,右链接指向大于两个父键的子键。

而2-3树就是一个含有2- 结点和3- 结点的二叉查找树。我们首先假设其能保证平衡,然后通过研究其插入方式来验证其是否是一个平衡树。插入分为如下几种情况:向2- 结点插入新键;向只有3- 结点的树中插入新键;向父节点为2- 结点的3- 结点插入新键;向父节点为3- 结点的3- 结点中插入新键。下面我们将逐步讨论。

  1. 向2- 结点插入新键。首先我们遍历到插入此键的位置,然后直接将这个需要插入的2- 结点变为一个3- 结点即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wWEEeqj-1593439290425)(/Users/liumengran/Library/Application Support/typora-user-images/image-20200628152852628.png)]

  1. 向只有3- 结点的树中插入新键:现将需要插入的3- 结点变为一个4- 结点,再将这个4- 结点变为3个2- 结点即可。

  1. 向父节点为2- 结点的3- 结点插入新键:这种方式有点类似于上面的方式,首先也是先变为4- 结点,然后将4- 结点变为3个2- 结点,再将三个2- 结点的父节点与父节点2- 结点组成一个3- 结点。

  1. 向父节点为3- 结点的3- 结点中插入新键:有了上面的经验我们就可以很简单的解决这个问题,只要我们不断的将3-结点向上递归修改,直到遇到一个2-结点,如果头结点也是一个3- 结点,那么我们只要将头结点变为一个2-结点即可,这样树依旧是平衡的,只不过树高增加了一个。

由插入操作我们可以看出,2-3树是从下向上生长的,而且总是一个平衡树。而且2-3树具有较高的性能,在一棵含有10亿个结点的2-3树中,我们最多只需要30次操作就可以进行查找和插入操作。

2-3树的性能是很好的,但是这种方式的数据结构在我们进行操作时会有很多不便,因为我们需要考虑的情况是在过多,而红黑树就可以很好的解决这个问题。


1.2.2 红黑树

红黑树是一种用标准二叉查找树(全部为2- 结点)加上一些额外信息(替换3- 结点)来表示2-3树。因为叫红黑树,所以我们将链接分为红连接和黑链接。通过红连接将两个2- 结点连接起来组成一个3- 结点,而黑链接则是2-3树中普通链接。这种方法的的好处是,我们无需修改就可以直接使用标准二叉查找树的**get()**方法。

红黑树的定义:

  • 红链接均为左链接
  • 没有任何一个结点同时和两条红链接相连
  • 该树是完美黑色平衡的,即任意空链接到跟路径上的黑链接数量相同

结点定义

因为涉及到红连接和黑链接,所以我们需要在二叉平衡树中新加入一个布尔变量来保存本结点与其父节点之间的链接的颜色

public class RedBlackBinarySelectTree<Key extends Comparable<Key>,Value>{/*** 储存红结点与黑结点的变量*/private static final boolean RED = true;private static final boolean BLACK = false;/*** 跟结点*/private Node root;/*** 内部类作为结点*/private class Node{private Key key;private Value value;//左结点private Node left;//右结点private Node right;//结点计数器private int N;private boolean color;/*** * @param key 键* @param value 值* @param n 结点计数器* @param color 指向结点的链接的颜色*/public Node(Key key, Value value, int n, boolean color) {this.key = key;this.value = value;N = n;this.color = color;}}/*** 判断指向结点的链接的颜色* @param node* @return*/private boolean isRed(Node node){if (node == null) {return false;}return node.color == RED;}
}

旋转操作

在我们实现一些操作的时候,可能会出现红色有链接和两条红色链接,我们需要通过一种名为旋转的操作进行修复,下面我们将分情况进行旋转的讨论

  • 右链接为红色

代码如下:

/*** 将链接颜色错误的结点进行修复* @param h 链接颜色错误的结点* @return 已经修复好的结点*/
Node rotateLeft(Node h){Node x = h.right;//交换顺序h.right = x.left;x.left = h;//保证指向此结点的颜色不变x.color = h.color;//h为红h.color = RED;x.N = h.N;h.N = size(h.left) + size(h.right) + 1;return x;
}
  • 连续两条链接为红色

此时我们需要先讲第二条链接旋转为右边为红色,然后再修复左右链接均为红色

代码如下:

/*** 左红链接转为右红链接* @param h 需要旋转的结点的父节点* @return 旋转后的结点*/
Node rotateRight(Node h){Node x = h.left;h.left = x.right;x.right = h;x.color = h.color;h.color = RED;x.N = h.N;h.N = size(h.left) + size(h.right) + 1;return x;
}
  • 左右链接均为红色

此时我们只需要将左右链接均变为黑色,然后将指向此结点的链接变为红色即可(将4- 结点变为3个2- 结点)

代码如下:

/*** 调整左右结点皆为红链接的结点* @param h 需要调整的结点*/
void flipColors(Node h){h.color = RED;h.left.color = BLACK;h.right.color = BLACK;
}

插入

  • 向2- 结点插入新键

向2- 结点插入新键分为两种情况,一种是插入其左结点,这样直接用一个红健与其相连即可,这样就相当于直接将2- 结点更新为3- 结点。但是如果需要插入其右结点,那么就会产生一个错误的右红链接,所以我们需要用一个rotateLeft()去旋转修复。

  • 向3- 结点插入新键

向3- 结点插入新键又有三种情况

  1. 需要插入的新键大雨原树中的两个键

此时新键需要插入到3- 结点的右结点,此时树是平衡的,有两个红健分别指向最大和最小的链接,此时我们只需要将这两个红健边给即可。

  1. 新键小于原树中的两个键

此时我们需要将新建插入3- 键的最左侧,即红健的左结点,此时树有两个相连的红结点,我们需要先将其右旋转,然后再将两个红健变黑即可。

  1. 新键介于原树中两个键之间

此时我们需要插入到3- 结点的中间位置,这样同样出现了两个连续的红结点,只不过这两个红结点一个在左结点,一个在右结点,所以我们需要先左旋转,再右旋转,再将红结点变黑

总的来说,我们在插入后会遇到以下三种情况

  • 右子节点是红色,左子节点是黑色:进行左旋转
  • 左子节点是红色,左子节点的左子节点也是红色:进行右旋转
  • 左子节点和右子节点都是红色:进行颜色转换

put()实现

有了上面的基础,我们就可以进行put()方法的实现,其实实现方式与二叉查找树区别不到,只是在我们插入之后要进行一番判断,判断的内容就是上文插入后的三种情况,再进行相应的操作,但是判断的顺序是什么样的呢?通过上文我们会发现,左旋转总是先于右旋转,而颜色转换总是最后一个。有了这些我们就能很容易的实现put()方法了。

/*** 查找为key 的结点,找到更新,未找到新键* @param key 键* @param value 值*/
public void put(Key key ,Value value){root = put(root ,key ,value);root.color = BLACK;
}/*** 查找为key的结点,找到更新,未找到新建* @param node 父节点* @param key 键* @param value 值* @return 父节点*/
private Node put(Node node ,Key key ,Value value){//如果为空则新建if (node == null){return new Node(key,value,1,RED);}int cmp = key.compareTo(node.key);if (cmp < 0){node.left = put(node.left, key, value);} else if (cmp > 0) {node.right = put(node.right ,key ,value);} else {node.value = value;}//判断插入后遇到的三种情况并进行相关操作//左黑右红,左旋转if (isRed(node.right) && !isRed(node.left)) {node = rotateLeft(node);} //左红子红if (isRed(node.left) && isRed(node.left.left)) {node = rotateRight(node);}//左红右红if (isRed(node.left) && isRed(node.right)) {flipColors(node);}node.N = size(node.left) + size(node.right) + 1;return node;
}

红黑树的性质

红黑树最大的优点就是可以将查找、插入、删除降低到了对数的级别,而且相比较于二叉查找树,其查找的效率更高,因为红黑树是一个完美平衡树(对应于2-3 树)。将千亿级别的数据操作,再几十次比较内就能实现,这无疑是一个巨大成就。

算法之红黑树/JAVA相关推荐

  1. 算法学习----红黑树

    算法学习----红黑树 红黑树的介绍 先来看下算法导论对R-BTree的介绍: 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子 ...

  2. 【算法】红黑树插入数据(变色,左旋、右旋)(二)

    本人菜鸡一只,正在更新红黑树系列的文章. 该系列已经全部更完,有5篇文章: [算法]红黑树(二叉树)概念与查询(一):https://blog.csdn.net/lsr40/article/detai ...

  3. 【算法】红黑树(二叉树)概念与查询(一)

    诶,算法这个东西,其实没那么简单,但是也没那么难. 红黑树,其实已经有很多大佬都整理过了,而且文章博客都写得超好,我写这篇文章的目的是:自己整理一次,这些知识才是自己的,否则永远是别人的~ 该系列已经 ...

  4. 红黑树-Java实现

    目录 一.定义 二.插入 三.删除 四.全部代码 五.颜色效果 一.定义 红黑树是特殊的平衡二叉树,具有以下特性: 1.根节点的颜色是黑色 2.节点颜色要么是黑色.要么是红色 3.如果一个节点的颜色是 ...

  5. Java数据结构与算法:红黑树

    概要 概述:R-B Tree,又称为"红黑树".本文参考了<算法导论>中红黑树相关知识,加之自己的理解,然后以图文的形式对红黑树进行说明.本文的主要内容包括:红黑树的特 ...

  6. 【数据结构与算法】红黑树的Java实现

    RedBlackTree原理分析 红黑树原理分析 RedBlackTree功能介绍 void insert(x) → Insert x void remove(x) → Remove x (unimp ...

  7. 《数据结构与算法之红黑树(Java实现)》

    说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正.若在阅 ...

  8. 红黑树+java+删除_红黑树深入剖析及Java实现

    红黑树是平衡二叉查找树的一种.为了深入理解红黑树,我们需要从二叉查找树开始讲起. BST 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小, ...

  9. 快手高级Java四轮面试题:设计模式+红黑树+Java锁+Redis等

    快手Java一面(一个小时十分钟) 1.自我介绍 2.说说B+树和B树的区别,优缺点等? 3聊聊Spring,主要IOC等等 4多线程JUC包下的一些常见的类,比如CountDownLatch.Sem ...

最新文章

  1. 深入理解HTML协议
  2. KA,连接池居然这么简单?
  3. 卡主 登录不上_香港服务器远程不上几大原因
  4. 利用Helm简化Kubernetes应用部署(1)
  5. MYSQL 查看表上索引的 1 方法
  6. 十八、数据容器、数据访问宽度、端口(计算机对数据处理方式:读取、写入、运算;数据可存放三个地方:CPU内部、内存、端口)
  7. 在远程系统上开发 SharePoint 应用程序
  8. Android数据库框架-ORMLite
  9. Cannot complete this action,please try again. Correlation ID :bd640a9d-4c19-doff-2fe0-6ce1104b59ae
  10. 前端框架中的大熊猫Ember
  11. CGB2202面向对象第10天
  12. 织梦免费网站模板手机端无法更新的原因及解决
  13. 人工智能 deepface 换脸技术 学习
  14. std::system
  15. 卫士处刑者冠军css3边,流放之路职业开荒路 决斗者冠军双持刀刃乱舞
  16. 简易爬虫-利用Python爬虫爬取圣墟小说到本地
  17. 2020 - 04 - 30 个人笔记
  18. UI设计前景如何?市场需要怎样的UI设计师?
  19. Laravel 中管道设计模式的使用 —— 中间件实现原理探究
  20. JAVA面向对象程序设计-FeiGe快递系统-继承III

热门文章

  1. MySQL —— 数据库基础
  2. MTK平台 SIM双卡改成单卡修改
  3. VirtualBox - 让分辨率自适应窗口大小
  4. 谷歌浏览器如何安装插件
  5. 11个销售心理学方法,帮你搞定客户!
  6. win10-未知的USB设备-解决自己问题的记录
  7. 为程序员更新了Joel测试
  8. Ubuntu 找不到wifi适配器
  9. 同学,你的系统吐司可能需要修复一下
  10. springboot admin自定义监控里的info信息