C#与数据结构--树论--红黑树(Red Black Tree)(下)

下面把代码贴出来,如果理解了上面所讲内容是很容易读懂这些代码的。

using System;
namespace PaintBST
{
public class RBTree : IBinaryTree //实现画树接口
{ //成员变量
private Node _head; //头指针
private Node[] path = new Node[32]; //记录访问路径上的结点
private int p; //表示当前访问到的结点在_path上的索引
INode IBinaryTree.Head //显式接口实现
{
get { return (INode)_head; }
}
public bool Add(int value) //添加一个元素
{ //如果是空树,则新结点成为二叉排序树的根
if (_head == null)
{
_head = new Node(value);
_head.IsRed = false;
return true;
}
p = 0;
//parent为上一次访问的结点,current为当前访问结点
Node parent = null, current = _head;
while (current != null)
{
path[p++] = current; //将路径上的结点插入数组
//如果插入值已存在,则插入失败
if (current.Data == value)
{
return false;
}
parent = current;
//当插入值小于当前结点,则继续访问左子树,否则访问右子树
current = (value < parent.Data) ? parent.Left : parent.Right;
}
current = new Node(value); //创建新结点
current.IsRed = true;
if (value < parent.Data) //如果插入值小于双亲结点的值
{
parent.Left = current; //成为左孩子
}
else //如果插入值大于双亲结点的值
{
parent.Right = current; //成为右孩子
}
if (!parent.IsRed)
{
return true;
}
path[p] = current;
//回溯并旋转
while ((p -= 2) >= 0) //现在p指向插入点的祖父结点
{
Node grandParent = path[p];
parent = path[p + 1];
if (!parent.IsRed)
{
break;
}
Node uncle = grandParent.Left == parent ? grandParent.Right : grandParent.Left;
current = path[p + 2];
if (IsRed(uncle)) //叔父存在并且为红色的情况
{
parent.IsRed = false;
uncle.IsRed = false;
if (p > 0) //如果祖父不是根结点,则将其染成红色
{
grandParent.IsRed = true;
}
}
else //叔父不存在或为黑的情况需要旋转
{
Node newRoot;
if (grandParent.Left == parent) //如果当前结点及父结点同为左孩子或右孩子
{
newRoot = (parent.Left == current) ? LL(grandParent) : LR(grandParent);
}
else
{
newRoot = (parent.Right == current) ? RR(grandParent) : RL(grandParent);
}
grandParent.IsRed = true; //祖父染成红色
newRoot.IsRed = false; //新根染成黑色
//将新根同曾祖父连接
ReplaceChildOfNodeOrRoot((p > 0) ? path[p - 1] : null, grandParent, newRoot);
return true; //旋转后不需要继续回溯,添加成功,直接退出
}
}
return true;
}
//旋转根旋转后换新根
private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild)
{
if (parent != null)
{
if (parent.Left == child)
{
parent.Left = newChild;
}
else
{
parent.Right = newChild;
}
}
else
{
_head = newChild;
}
}
private static bool IsBlack(Node node)
{
return ((node != null) && !node.IsRed);
}
private static bool IsNullOrBlack(Node node)
{
if (node != null)
{
return !node.IsRed;
}
return true;
}
private static bool IsRed(Node node)
{
return ((node != null) && node.IsRed);
}
//删除指定值
public bool Remove(int value)
{
p = -1;
//parent表示双亲结点,node表示当前结点
Node node = _head;
//寻找指定值所在的结点
while (node != null)
{
path[++p] = node;
//如果找到,则调用RemoveNode方法删除结点
if (value == node.Data)
{
RemoveNode(node);//现在p指向被删除结点
return true; //返回true表示删除成功
}
if (value < node.Data)
{ //如果删除值小于当前结点,则向左子树继续寻找
node = node.Left;
}
else
{ //如果删除值大于当前结点,则向右子树继续寻找
node = node.Right;
}
}
return false; //返回false表示删除失败
}
//删除指定结点
private void RemoveNode(Node node)
{
Node tmp = null; //tmp最终指向实际被删除的结点
//当被删除结点存在左右子树时
if (node.Left != null && node.Right != null)
{
tmp = node.Left; //获取左子树
path[++p] = tmp;
while (tmp.Right != null) //获取node的中序遍历前驱结点,并存放于tmp中
{ //找到左子树中的最右下结点
tmp = tmp.Right;
path[++p] = tmp;
}
//用中序遍历前驱结点的值代替被删除结点的值
node.Data = tmp.Data;
}
else
{
tmp = node;
}
//当只有左子树或右子树或为叶子结点时
//首先找到惟一的孩子结点
Node newTmp = tmp.Left;
if (newTmp == null) //如果只有右孩子或没孩子
{
newTmp = tmp.Right;
}
if (p > 0)
{
Node parent = path[p - 1];
if (parent.Left == tmp)
{ //如果被删结点是左孩子
parent.Left = newTmp;
}
else
{ //如果被删结点是右孩子
parent.Right = newTmp;
}
if (!tmp.IsRed && IsRed(newTmp))
{
newTmp.IsRed = false;
return;
}
}
else //当删除的是根结点时
{
_head = newTmp;
if (_head != null)
{
_head.IsRed = false;
}
return;
}
path[p] = newTmp;
//如果被删除的是红色结点,则它必定是叶子结点,删除成功,返回
if (IsRed(tmp))
{
return;
}
//删除完后进行旋转,现在p指向实际被删除的位置,这个位置可能为空,tmp指向被删除的旧点,
while (p > 0)
{ //剩下的是双黑的情况
//首先找到兄弟结点
Node current = path[p];
Node parent = path[p - 1];
bool currentIsLeft = (parent.Left == current);
Node sibling = currentIsLeft ? parent.Right : parent.Left;
//红兄的情况,需要LL旋转或RR旋转
if (IsRed(sibling))
{
Node newRoot;
if (currentIsLeft)
{
newRoot = RR(parent);
}
else
{
newRoot = LL(parent);
}
ReplaceChildOfNodeOrRoot(p > 1 ? path[p - 2] : null, parent, newRoot);
sibling.IsRed = false;
parent.IsRed = true;
//回溯点降低
path[p - 1] = newRoot;
path[p] = parent;
path[++p] = current;
}
else //黑兄的情况
{
//黑兄二黑侄
if (IsNullOrBlack(sibling.Left) && IsNullOrBlack(sibling.Right))
{ //红父的情况
if (parent.IsRed)
{
parent.IsRed = false;
sibling.IsRed = true;
if (current != null)
{
current.IsRed = false;
}
break; //删除成功
}
else //黑父的情况
{
parent.IsRed = IsRed(current);
if (current != null)
{
current.IsRed = false;
}
sibling.IsRed = true;
p--; //需要继续回溯
}
}
else //黑兄红侄
{
Node newRoot;
if (currentIsLeft) //兄弟在右边
{
if (IsRed(sibling.Right)) //红侄在右边
{ //RR型旋转
newRoot = RR(parent);
sibling.Right.IsRed = false;
}
else
{ //RL型旋转
newRoot = RL(parent);
}
}
else //兄弟在左边
{
if (IsRed(sibling.Left))
{ //LL型旋转
newRoot = LL(parent);
sibling.Left.IsRed = false;
}
else
{ //LR型旋转
newRoot = LR(parent);
}
}
if (current != null)
{
current.IsRed = false;
}
newRoot.IsRed = parent.IsRed;
parent.IsRed = false;
ReplaceChildOfNodeOrRoot(p > 1 ? path[p - 2] : null, parent, newRoot);
break; //不需要回溯,删除成功
}
}
}
}
//root为旋转根,rootPrev为旋转根双亲结点
private Node LL(Node root) //LL型旋转,返回旋转后的新子树根
{
Node left = root.Left;
root.Left = left.Right;
left.Right = root;
return left;
}
private Node LR(Node root) //LR型旋转,返回旋转后的新子树根
{
Node left = root.Left;
Node right = left.Right;
root.Left = right.Right;
right.Right = root;
left.Right = right.Left;
right.Left = left;
return right;
}
private Node RR(Node root) //RR型旋转,返回旋转后的新子树根
{
Node right = root.Right;
root.Right = right.Left;
right.Left = root;
return right;
}
private Node RL(Node root) //RL型旋转,返回旋转后的新子树根
{
Node right = root.Right;
Node left = right.Left;
root.Right = left.Left;
left.Left = root;
right.Left = left.Right;
left.Right = right;
return left;
}
}
}

时间: 2024-08-03 14:06:55

C#与数据结构--树论--红黑树(Red Black Tree)(下)的相关文章

C#与数据结构--树论--红黑树(Red Black Tree)(上)

介绍 今天我们来介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf Bayer于1972年发明,当时被称为平衡二叉B树(symmetric binary B-trees),1978年被Leonidas J. Guibas 和 Robert Sedgewick改成一个比较摩登的名字:红黑树. 红黑树和之前所讲的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.自从红黑树出来后,AVL树就被放到了博物馆里,据说是红黑树有

浅谈算法和数据结构 九 平衡查找树之红黑树

前面一篇文章介绍了2-3查找树,可以看到,2-3查找树能保证在插入元素之后能保持树的平衡状态,最坏情况下即所有的子节点都是2-node,树的高度为lgN,从而保证了最坏情况下的时间复杂度.但是2-3树实现起来比较复杂,本文介绍一种简单实现2-3树的数据结构,即红黑树(Red-Black Tree) 定义 红黑树的主要是像是对2-3查找树进行编码,尤其是对2-3查找树中的3-nodes节点添加额外的信息.红黑树中将节点之间的链接分为两种不同类型,红色链接,他用来链接两个2-nodes节点来表示一个

数据结构之红黑树

概述 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组.它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees).红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 二叉查找树(BST) 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右

数据结构之红黑树详解_C 语言

1.简介 红黑树是一种自平衡二叉查找树.它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用.在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持).它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除等操作. 本文介绍了红黑树的基本性质和基本操作. 2.红黑树

java中treemap和treeset实现(红黑树)

TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 TreeSet 之间的关系,下面先看 TreeSet 类的部分源代码: public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializab

红黑树深入剖析及Java实现

红黑树是平衡二叉查找树的一种.为了深入理解红黑树,我们需要从二叉查找树开始讲起. BST 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右节点的值要比父节点的值大.它的高度决定了它的查找效率. 在理想的情况下,二叉查找树增删查改的时间复杂度为O(logN)(其中N为节点数),最坏的情况下为O(N).当它的高度为logN+1时,我们就说二叉查找树是平衡的. BST的查找操作 T  key = a search key  Node ro

面试题——轻松搞定面试中的红黑树问题

版权所有,转载请注明出处,谢谢!http://blog.csdn.net/silangquan/article/details/18655795      连续两次面试都问到了红黑树,关键两次都没有答好,这次就完整地来学习整理一下. 没有学习过红黑树的同学请参考: <<Introduction to Algorithms>> Chapter 13 Red-Black Trees Chapter 14 Augmenting Data Structures 教你透彻了解红黑树    1

红黑树解法的why而非how

0 初衷 很多介绍红黑树的文章如同算法导论书中那样,都是上来直接给出一些分类情况,以及每个分类情况下的处理办法,而没有着重讲述为什么这么分类,为什么这个分类下执行这些操作,即只介绍了how,没有重点给出why.本篇文章的重点就在于解释why. 这样可能就导致一种现象:我按照这些分类以及分类下的操作办法,的确完整的走通想通了整个算法过程,感觉应该理解红黑树了,但是可能过了几个月后就忘记如何分类的了,忘记分类下如何操作的了. 归根到底是我们没有找出最本质的东西,比如说插入节点时遇到父子是红红的问题,

java集合类TreeMap和TreeSet及红黑树

看这篇博客前,我觉得很有必要先看下我之前的几篇博客 Red-Black Trees(红黑树)                                         (TreeMap底层的实现就是用的红黑树数据结构) 探索equals()和hashCode()方法                                 (TreeMap/TreeSet实现使用到的核心方法) java中的HashTable,HashMap和HashSet      (同为java集合类,对比下他们