Java数据结构——红黑树

红黑树介绍
红黑树(Red-Black Tree),它一种特殊的二叉查找树。
红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(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.红黑树牺牲了平衡性,综合增删改查的所有操作,红黑树的统计性能较优。

原文地址:https://www.cnblogs.com/ericz2j/p/10751507.html

时间: 2024-10-13 02:44:11

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

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

红黑树相比平衡二叉树(AVL)是一种弱平衡树,且具有以下特性: 1.每个节点非红即黑; 2.根节点是黑的; 3.每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的; 4.如图所示,如果一个节点是红的,那么它的两儿子都是黑的; 5.对于任意节点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑节点; 6.每条路径都包含相同的黑节点 原文地址:https://www.cnblogs.com/hardhp74520/p/11317028.html

数据结构 - 红黑树(Red Black Tree)插入详解与实现(Java)

最终还是决定把红黑树的篇章一分为二,插入操作一篇,删除操作一篇,因为合在一起写篇幅实在太长了,写起来都觉得累,何况是阅读并理解的读者. 红黑树删除操作请参考 数据结构 - 红黑树(Red Black Tree)删除详解与实现(Java) 现在网络上最不缺的就是对某个知识点的讲解博文,各种花样标题百出,更有类似"一文讲懂xxx","史上最简单的xxx讲解","xxx看了还不懂你打我"之类云云.其中也不乏有些理论甚至是举例都雷同的两篇不同文章,至于作

Java实现红黑树

转自:http://www.cnblogs.com/skywang12345/p/3624343.html 红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值.除了具备该特性之外,红黑树还包括许多额外的信息. 红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black).红黑树的特性:(1) 每个节点或者

数据结构-红黑树详解

介绍: 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees).后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的"红黑树". 红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 它虽然

数据结构-红黑树

转自:http://dongxicheng.org/structure/red-black-tree/ 1. 简介 红黑树是一种自平衡二叉查找树.它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用.在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持).它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它

数据结构 - 红黑树学习

红黑树 红黑树算是用的比较多,但是平时自己很少写的一种数据结构了,先看下介绍: 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 看~ 典型是用来实现关联数组,能想到什么数据结构呢?list map set这些容器的底层都是红黑树来实现的. 红黑树和AVL树(平衡二叉查找树) 红黑树并不是完全平衡的一棵树,所以红黑树是在平均时间(经验)上为O(log n)的复杂度,包括插入,删除和查找. 红黑树规则 所有节点都分为红色

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

首先,在阅读文章之前,我希望读者对二叉树有一定的了解,因为红黑树的本质就是一颗二叉树.所以本篇博客中不在将二叉树的增删查的基本操作了,需要了解的同学可以到我之前写的一篇关于二叉树基本操作的博客:https://www.cnblogs.com/rainple/p/9970760.html: 有随机数节点组成的二叉树的平均高度为logn,所以正常情况下二叉树查找的时间复杂度为O(logn).但是,根据二叉树的特性,在最坏的情况下,比如存储的是一个有序的数据的话,那么所以的数据都会形成一条链,此时二叉

数据结构——红黑树

红黑树是二叉排序树的改进, 红黑树有几个特点: 1:节点只有2中颜色,红色和黑色. 2:根节点一定是黑色节点. 3:红色节点的子节点一定是黑色节点. 4:黑色高度(根节点到每个叶子节点的路径长度包含相同的黑色节点)相等. 规定的插入的节点一定是红色节点, 红黑树的插入节点后需要调整的规则,插入节点需要调整的情况有3种: 情况1:插入的节点的父节点和叔叔节点都为红色: 以上情况节点4为插入节点(当前节点),这种情况调整方式是将父节点和叔叔节点都调整为黑色节点,祖父节点调整为红色,将祖父节点变为当前

数据结构-红黑树(Red-Black Tree)的C++实现模板

红黑树的实现还真不简单,各种染色旋转足足折腾了笔者几天.. 不过收获也是巨大的.笔者现在终于明白为啥二叉搜索树这么重要了,确实很有用. 下面上代码. 细心的朋友可能会觉得似乎少了那么几个接口,没错,因为 Precessor(求前驱) / Successor(求后继) / getMaximum (求树中最大值)/ getMinimum(求树中最小值)/ Inorder Traversal(中序遍历)/ Postorder Traversal(后序遍历) 这些操作都可以直接用笔者二叉搜索树(BST)