红黑树介绍
红黑树(Red-Black Tree),它一种特殊的二叉查找树。执行查找、插入、删除等操作的时间复杂度为O(logn)。

红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
红黑树的特性:

  1. 每个节点或者是黑色,或者是红色。
  2. 根节点是黑色。
  3. 每个叶子节点是黑色。 (注意:这里叶子节点,是指为空的叶子节点)
  4. 如果一个节点是红色的,则它的子节点必须是黑色的。
  5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

关于红黑树,需要注意的是:

  1. 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
  2. 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
  3. 红黑树是保持“黑平衡”的二叉树,严格意义上来说,它并不是真正的平衡二叉树,因为它可以不满足平衡的定义,即左右子树高度差不大于1,
  4. 红黑树的最大高度是2logn,它的复杂度是O(logn),与AVL树的复杂度一样。

红黑树的基本操作
红黑树的基本操作是添加、删除和旋转。在对红黑树进行添加或删除后,会用到旋转方法。因为添加或删除红黑树中的节点之后,红黑树会发生变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别图解对红黑树的基本操作进行介绍。

左旋、右旋操作

对x进行左旋,意味着"将x变成一个左节点"。
对y进行右旋,意味着"将y变成一个右节点"。

// 左旋转过程
// node x
// / \ 左旋转 / \
// T1 x -----> node T3
// / \ / \
// T2 T3 T1 T2
private TreeNode leftRotate(TreeNode node) {
TreeNode x = node.rightChild;
// 左旋转
node.rightChild = x.leftChild;
x.leftChild = node;
x.color = node.color;
node.color = RED; return x;
} // 右旋转过程
// node x
// / \ 右旋转 / \
// x T1 -----> y node
// / \ / \
// y T2 T1 T2
private TreeNode rightRotate(TreeNode node) {
TreeNode x = node.leftChild;
// 右旋转
node.leftChild = x.rightChild;
x.rightChild = node;
x.color = node.color;
node.color = RED; return x;
}

颜色翻转操作

// 颜色翻转
private void flipColors(TreeNode node) {
node.color = RED;
node.leftChild.color = BLACK;
node.rightChild.color = BLACK;
}

添加操作

将一个节点插入到红黑树中的步骤:

  1. 将红黑树当作一颗二叉查找树,将节点插入。
  2. 将插入的节点着色为"红色"。
  3. 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。

// 向红黑树中添加节点
public void add(int data) {
root = add(root, data);
root.color = BLACK;
} // 添加节点后返回红黑树的根节点
public TreeNode add(TreeNode node, int data) {
if (node == null) {
TreeNode treeNode = new TreeNode(data);
return treeNode;
} else {
if (data < node.data) {
node.leftChild = add(node.leftChild, data);
} else if (data > node.data) {
node.rightChild = add(node.rightChild, data);
} else {
node.data = data;
}
// node
// \
// 红
if (isRED(node.rightChild) && !isRED(node.leftChild)) {
node = leftRotate(node);
}
// node
// /
// 红
// /
// 红
if (isRED(node.leftChild) && isRED(node.leftChild.leftChild)) {
node = rightRotate(node);
}
// node
// / \
// 红 红
if (isRED(node.leftChild) && isRED(node.rightChild)) {
flipColors(node);
}
return node;
}
}

查找操作

// 查找节点
public TreeNode search(int data) {
TreeNode current = root;
while (current.data != data) {
if (data < current.data) {
current = current.leftChild;
} else {
current = current.rightChild;
}
if (current == null) {
return null;
}
}
return current;
}

删除操作
将红黑树内的某一个节点删除的步骤

  1. 将红黑树当作一颗二叉查找树,将节点删除。
  2. 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
  3. 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
  4. 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。 在被删除节点有两个非空子节点的情况下,它的后继节点不可能是双子非空,意味着该节点的后继节点要么没有儿子,要么只有一个儿子。若没有儿子,则按情况①进行处理;若只有一个儿子,则按情况②进行处理。
  5. 通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。

/*
* 删除结点(node),并返回被删除的结点
*/
private void remove(TreeNode node) {
TreeNode child, parent;
boolean color;
// 被删除节点的左右孩子都不为空时
if ( (node.leftChild!=null) && (node.rightChild!=null) ) {
// 被删节点的后继节点取代"被删节点"的位置,然后再将被删节点去掉。
TreeNode replace = node;
// 获取后继节点
replace = replace.rightChild;
while (replace.leftChild != null)
replace = replace.leftChild;
// node不是根节点
if (parentOf(node)!=null) {
if (parentOf(node).leftChild == node)
parentOf(node).leftChild = replace;
else
parentOf(node).rightChild = replace;
} else {
// node是根节点,更新根节点。
this.root = replace;
}
// child是取代节点的右孩子,也是需要调整的节点。
// 取代节点肯定不存在左孩子,因为它是一个后继节点。
child = replace.rightChild;
parent = parentOf(replace);
// 保存取代节点的颜色
color = colorOf(replace);
// 被删除节点是它的后继节点的父节点
if (parent == node) {
parent = replace;
} else {
// child不为空
if (child!=null)
setParent(child, parent);
parent.leftChild = child; replace.rightChild = node.rightChild;
setParent(node.rightChild, replace);
}
replace.parent = node.parent;
replace.color = node.color;
replace.leftChild = node.leftChild;
node.leftChild.parent = replace;
if (color == BLACK)
removeFixUp(child, parent);
node = null;
return ;
}
if (node.leftChild !=null) {
child = node.leftChild;
} else {
child = node.rightChild;
}
parent = node.parent;
// 保存取代节点的颜色
color = node.color;
if (child!=null)
child.parent = parent;
// node不是根节点
if (parent!=null) {
if (parent.leftChild == node)
parent.leftChild = child;
else
parent.rightChild = child;
} else {
this.root = child;
}
if (color == BLACK)
removeFixUp(child, parent);
node = null;
} public void remove(int data) {
TreeNode node;
if ((node = search(data)) != null)
remove(node);
} /*
* 红黑树删除修正函数
* 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*/
private void removeFixUp(TreeNode node, TreeNode parent) {
TreeNode other;
while ((node==null || isBlack(node)) && (node != this.root)) {
if (parent.leftChild == node) {
other = parent.rightChild;
if (isRed(other)) {
//x的兄弟是红色的
setBlack(other);
setRed(parent);
leftRotate(parent);
other = parent.rightChild;
} if ((other.leftChild==null || isBlack(other.leftChild)) &&
(other.rightChild==null || isBlack(other.rightChild))) {
//x的兄弟是黑色,且兄弟的两个孩子也是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.rightChild==null || isBlack(other.rightChild)) {
//x的兄弟是黑色的,并且兄弟的左孩子是红色,右孩子为黑色。
setBlack(other.leftChild);
setRed(other);
rightRotate(other);
other = parent.rightChild;
}
//x的兄弟是黑色的;并且兄弟的右孩子是红色的,左孩子任意颜色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.rightChild);
leftRotate(parent);
node = this.root;
break;
}
} else {
other = parent.leftChild;
if (isRed(other)) {
//x的兄弟是红色的
setBlack(other);
setRed(parent);
rightRotate(parent);
other = parent.leftChild;
}
if ((other.leftChild==null || isBlack(other.leftChild)) &&
(other.rightChild==null || isBlack(other.rightChild))) {
//x的兄弟是黑色,且兄弟的两个孩子也都是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.leftChild==null || isBlack(other.leftChild)) {
// x的兄弟是黑色的,并且兄弟的左孩子是红色,右孩子为黑色。
setBlack(other.rightChild);
setRed(other);
leftRotate(other);
other = parent.leftChild;
}
// x的兄弟是黑色的;并且兄弟的右孩子是红色的,左孩子任意颜色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.leftChild);
rightRotate(parent);
node = this.root;
break;
}
}
}
if (node!=null)
setBlack(node);
}

红黑树性能总结

1.对于完全随机的数据,普通的二叉搜索树效率较高,但极端下会退化成链表;

2.对于查询较多的情况,AVL树效率较高;

3.红黑树牺牲了平衡性,综合增删改查的所有操作,红黑树的统计性能较优。

Java数据结构——红黑树的更多相关文章

  1. java数据结构——红黑树(R-B Tree)

    红黑树相比平衡二叉树(AVL)是一种弱平衡树,且具有以下特性: 1.每个节点非红即黑; 2.根节点是黑的; 3.每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的; 4.如图所示,如果一个 ...

  2. Java实现红黑树

    转自:http://www.cnblogs.com/skywang12345/p/3624343.html 红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉 ...

  3. 高级数据结构---红黑树及其插入左旋右旋代码java实现

    前面我们说到的二叉查找树,可以看到根结点是初始化之后就是固定了的,后续插入的数如果都比它大,或者都比它小,那么这个时候它就退化成了链表了,查询的时间复杂度就变成了O(n),而不是理想中O(logn), ...

  4. Java 集合 | 红黑树 | 前置知识

    一.前言 0tnv1e.png 为啥要学红黑树吖? 因为笔者最近在赶项目的时候,不忘抽出时间来复习 Java 基础知识,现在准备看集合的源码啦啦.听闻,HashMap 在 jdk 1.8 的时候,底层 ...

  5. 基于Java实现红黑树的基本操作

    首先,在阅读文章之前,我希望读者对二叉树有一定的了解,因为红黑树的本质就是一颗二叉树.所以本篇博客中不在将二叉树的增删查的基本操作了,需要了解的同学可以到我之前写的一篇关于二叉树基本操作的博客:htt ...

  6. 第三十三篇 玩转数据结构——红黑树(Read Black Tree)

    1.. 图解2-3树维持绝对平衡的原理: 2.. 红黑树与2-3树是等价的 3.. 红黑树的特点 简要概括如下: 所有节点非黑即红:根节点为黑:NULL节点为黑:红节点孩子为黑:黑平衡 4.. 实现红 ...

  7. Java数据结构和算法(二)顺序存储的树结构

    Java数据结构和算法(二)顺序存储的树结构 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 二叉树也可以用数组存储,可以和完 ...

  8. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  9. 红黑树(五)之 Java的实现

    概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...

随机推荐

  1. JAVA锁的膨胀过程和优化

    首先说一下锁的优化策略. 1,自旋锁 自选锁其实就是在拿锁时发现已经有线程拿了锁,自己如果去拿会阻塞自己,这个时候会选择进行一次忙循环尝试.也就是不停循环看是否能等到上个线程自己释放锁.这个问题是基于 ...

  2. Linux企业级项目实践之网络爬虫(4)——主程序流程

    当我们设计好程序框架之后就要开始实现它了.第一步当然是要实现主程序的流程框架.之后我们逐渐填充每个流程的细节和其需要调用的模块. 主程序的流程如下: 1.  解析命令行参数,并根据参数跳转到相应的处理 ...

  3. Spring MVC执行流程

    SpringMVC是隶属于Spring Web中的一部分, 属于表现层的框架. 其使用了MVC架构模式的思想, 将Web层进行职责解耦, 使用请求-响应模型简化Web开发 SpringMVC通过中央调 ...

  4. 大数据处理架构hadoop

    Hadoop简介 Hadoop是Apache软件基金会旗下的一个开源分布式计算平台,为用户提供了系统底层细节透明的分布式基础架构.它是基于java语言开发的,具有很好的跨平台特性,其核心是分布式文件系 ...

  5. 2017-4-13/MySQL

    1. mysql一般的连接方式都有哪些,各自优缺点. MySQL:过程式风格,最常用. MySQLi:MySQL的增强扩展,提供了过程化和面向对象两种风格的API,增加了预编译和参数绑定等新特性, 但 ...

  6. .NET发送邮件的方法

    整理一下,在.NET中发送邮件的一个方法,代码如下: public static string Net_Email(string strSendto, string strCC, string str ...

  7. H - Tickets dp

    题目链接: https://cn.vjudge.net/contest/68966#problem/H AC代码; #include<iostream> #include<strin ...

  8. 缓存管理(本地缓存+memcached)

    http://www.cnblogs.com/daizhj/archive/2009/11/17/1604436.html

  9. day 82 Vue学习二之vue结合项目简单使用、this指向问题

    Vue学习二之vue结合项目简单使用.this指向问题   本节目录 一 阶段性项目流程梳理 二 vue切换图片 三 vue中使用ajax 四 vue实现音乐播放器 五 vue的计算属性和监听器 六 ...

  10. 使用opencv3+python实现视频运动目标检测

    本文主要实现了伯乐在线上的一个实践小项目,原文链接,用以巩固opencv视频操作知识内容.整个项目均有代码注释,通俗易懂,短短几十行就可以达到还算不错的实现效果,做起来成就感满满哒.打开编辑器,一起来 ...