您的位置:首页 > 理论基础 > 数据结构算法

红黑树--(下)

2016-07-09 13:24 537 查看
红黑树的删除操作

    上次博客中主要说了红黑树的插入,遍历,判断是否合法等方法,当时对红黑树的删除操作还没有研究好,所以这些天一直在研究红黑树的删除操作。经过几天的反复画图,调试,查看书籍和资料,终于完成了红黑树的删除操作,所以准备分享一下成果。

     这里先说明的是,我实现的是不带哨兵位的红黑树(这个哨兵位其实是STL中红黑树中实现的一个特殊节点,主要是用来实现迭代器的,这里就不多多说明),总之我的红黑树没有多余的节点。

     我们都知道在红黑树方面的知识方面,七月大神写了好几篇博客介绍,我也参考了一下。他的方法是先将待删除的节点替换掉(因为有哨兵节点且颜色为黑色,所以替换后不会存在NULL等等因素的影响),然后再调整我是先保留待删除的节点,等待调整完以后再删除待删除的节点(因为没有哨兵节点,所以需要先保留,参与调整过程)。真是因为实现方法不同,我在看了他的代码以后真的是很懵,差别真的很大,于是我仔细看了他的情况分析,然后我对照这些情形,分析我的实现方式可能出现的情况(我的实现方式确实要考虑的情况更多,因为指针操作很可能会访问空节点而崩溃,刚刚开始的时候真的是各种访问指针出错)。

    首先分析一下删除节点时可能出现的各种情况吧:









    其实可能出现的情况基本相似,但是我的实现里面在很多地方需要考虑到空节点的情况,所以在很多地方需要保证不会因为访问空节点而导致程序崩溃的情况,具体细节请参看我的代码,代码将会在后面附上。下面我就演示一下,红黑树删除节点的过程吧(这个过程是我根据我的程序删除过程画的):

插入节点的顺序为:4, 19, 0, 17, 10, 12, 8, 11, 18, 1, 6, 16, 7, 14, 9, 3, 13, 2, 5, 15


删除结点的顺序也为:4, 19, 0, 17, 10, 12, 8, 11, 18, 1, 6, 16, 7, 14, 9, 3, 13, 2, 5, 15





























































































   

    上面就是完整删除一颗红黑树全部节点的过程。在我电脑上的运行结果如下,我是每次删除一个节点后判断一次该红黑树是否是合法的,并且打印的中序遍历的结果,可以看出这棵红黑树的插入和删除都是正确的。



       完成这个过程后,我感觉整个红黑树的难点部分已经完成,置于某些方法,比如:求树的高度啊,求深度,先序遍历,后序遍历等问题,我觉得相对来说比较简单一些吧,在二叉树部分我也基本能上实现过,为了使代码量不至于太多(光实现了这几个功能就有500多行代码了)而不便于阅读,我就省略了哪些其他方法的实现。

       下面我就把代码附上吧,光光有图还真的不能说明真相,还需要代码。代码可能不是很简练,但是可读性还是可以的。

#pragma once

#include <iostream>
using namespace std;

enum Color
{
Red,
Black
};

template<class K,class V>
struct RedBlackTreeNode
{
K _key; //键
V _val; //值
Color _color; //颜色
RedBlackTreeNode<K, V>* _parent; //父节点
RedBlackTreeNode<K, V>* _left; //左子节点
RedBlackTreeNode<K, V>* _right; //右子节点

RedBlackTreeNode(const K& key, const V& val)
: _key(key)
, _val(val)
, _color(Red)
, _parent(NULL)
, _left(NULL)
, _right(NULL)
{}
};

template<class K,class V>
class RedBlackTree
{
typedef RedBlackTreeNode<K, V> Node;
public:
RedBlackTree()
:_root(NULL)
{}

void InOrder()
{
_InOrder(_root);
}

Node* Find(const K &key)
{
Node *cur = _root;
while (cur)
{
if (cur->_key == key)
return cur;
else if(key < cur->_key)
cur = cur->_left;
else
cur = cur->_right;
}
return cur;
}

bool Insert(const K &key, const V &val)
{
if (_root == NULL)
{
_root = new Node(key, val);
_root->_color = Black;
return true;
}

//找插入点
Node *parent = NULL;
Node *cur = _root;
while (cur)
{
parent = cur;
if (cur->_key == key)
return false;
else if (cur->_key < key)
cur = cur->_right;
else
cur = cur->_left;
}

//连接节点
cur = new Node(key, val);
cur->_parent = parent;
if (parent->_key > key)
parent->_left = cur;
else
parent->_right = cur;

//根据红黑树条件调节
while (parent != _root && parent->_color == Red) //祖父节点存在且父节点为红色
{
Node* grandfa = parent->_parent;
if (grandfa->_left == parent)
{ //新插入节点位于grandfa的左子树
Node *uncle = grandfa->_right;
if (uncle && uncle->_color == Red)
{
grandfa->_color = Red;
parent->_color = Black;
uncle->_color = Black;

cur = grandfa;
if (cur == _root)
{//遍历到根了
break;
}
parent = cur->_parent;
//continue;
}
else
{
if (cur == parent->_right)
{//左单旋
_RotateL(parent);
cur = cur->_left;
parent = cur->_parent;
}
//右单旋
_RotateR(grandfa);
parent->_color = Black;
parent->_left->_color = Red;
parent->_right->_color = Red;
break;
}
}
else
{ //新插入节点位于grandfa的右子树
Node *uncle = grandfa->_left;
if (uncle && uncle->_color == Red)
{
grandfa->_color = Red;
parent->_color = Black;
uncle->_color = Black;

cur = grandfa;
if (cur == _root)
{
break;
}
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{ //右单旋
_RotateR(parent);
cur = cur->_right;
parent = cur->_parent;
}
//左单旋
_RotateL(grandfa);
parent->_color = Black;
parent->_left->_color = Red;
parent->_right->_color = Red;
break;
}
}
}
_root->_color = Black;
return true;
}

bool Remove(const K &key)
{
/*
我的实现的是没有哨兵节点的,所以我采用的方法是先调整再删除
*/
Node* delNode = Find(key);
if (delNode == NULL)
return false;

if (delNode->_left && delNode->_right)
{ //左右都不为NULL
Node *replace = delNode->_right; //找替代删除的节点
while (replace->_left)
replace = replace->_left;

delNode->_key = replace->_key;
delNode->_val = replace->_val;
delNode = replace;
}
else if (delNode->_right && delNode->_right->_color == Red)
{
Node* delR = delNode->_right;

if (_root == delNode)
_root = delR;

delR->_color = delNode->_color;
delR->_parent = delNode->_parent;

if (delNode->_parent)
{
if (delNode->_parent->_left == delNode)
delNode->_parent->_left = delR;
else
delNode->_parent->_right = delR;
}

delete delNode;
return true;
}
else if (delNode->_left && delNode->_left->_color == Red)
{
Node* delL = delNode->_left;
if (_root == delNode)
_root = delL;

delL->_color = delNode->_color;
delL->_parent = delNode->_parent;
if (delNode->_parent)
{
if (delNode->_parent->_left == delNode)
delNode->_parent->_left = delL;
else
delNode->_parent->_right = delL;
}

delete delNode;
return true;
}

if (delNode->_color == Black)
{
DelFixUp(delNode);
}

if (delNode->_left == NULL && delNode->_right == NULL)
{
Node *parent = delNode->_parent;
if (parent)
{
if (parent->_left == delNode)
parent->_left = NULL;
else
parent->_right = NULL;
}

if (_root == delNode)
_root = NULL;

delNode->_parent = NULL;
}
else if (delNode->_left == NULL)
{
Node *parent = delNode->_parent;
if (parent)
{
if (parent->_left == delNode)
{
parent->_left = delNode->_right;
delNode->_right->_parent = parent;
}
else
{
parent->_right = delNode->_right;
delNode->_right->_parent = parent;
}
}
}
else
{
Node *parent = delNode->_parent;
delNode->_left->_color = Black;
if (parent)
{
if (parent->_left == delNode)
{
parent->_left = delNode->_left;
delNode->_left->_parent = parent;
}
else
{
parent->_right = delNode->_left;
delNode->_left->_parent = parent;
}
}
}

delete delNode;
return true;
}

bool IsRight()
{
//统计一条路径上的黑色节点的个数
Node *cur = _root;
int blacknum = 0;
while (cur)
{
if (cur->_color == Black)
++blacknum;
cur = cur->_left;
}

return _CheckRBTree(_root,blacknum,0);
}

~RedBlackTree()
{}

protected:
void DelFixUp(Node* node)
{
while (node != _root && node->_color == Black)
{
Node* pNode = node->_parent;

if (node == node->_parent->_left)
{ //左孩子
Node * brother = pNode->_right; //兄弟节点
//if (brother)
//{ //兄弟节点存在(兄弟节点必定存在兄弟节点)
if (brother->_color == Red)
{ //情况1: 兄弟节点为红色
brother->_color = Black;
Node* parent = node->_parent;
parent->_color = Red;
_RotateL(parent);
}
else
{ //兄弟节点为黑色
Node *left = brother->_left; //兄弟节点的左孩子
Node *right = brother->_right; //兄弟节点的右孩子

if ((!left && !right) || ((left && left->_color == Black) && (right && right->_color == Black)))
{//情况2:兄弟节点的左右孩子均为黑色或者都不存在,
brother->_color = Red;
node = node->_parent;
if (brother->_parent->_color == Black)
{
continue;
}
}
else if ((!right) || right->_color == Black)
{ //情况3:兄弟节点的右孩子为NULL或者颜色为黑色,
//右孩子为NULL的时候左孩子必定存在,且为红色
//右孩子为黑色时左孩子必定存在且未红色(上面的条件考虑了它们都为黑色的情况)
Node* parent = left->_parent;
left->_color = Black;
parent->_color = Red;
_RotateR(parent);
brother = brother->_parent;
}
//else if ((!left) || left->_color == Black )
//{ //brother的左孩子不存在,则右孩子必存在且颜色为红,左孩子若存在则颜色必为红

if (brother->_left == NULL && brother->_right == NULL && pNode->_right == brother)
{ //这个时候是兄弟节点左右孩子都不存在的情况,
node->_color = Black;
node = _root;
}
else
{
if (node != _root)
{
brother->_color = node->_parent->_color;
node->_parent->_color = Black;
}
else
{
brother->_color = Black;
}
brother->_right->_color = Black;
Node* parentB = brother->_parent;
_RotateL(parentB);
node = _root;
}
//}
}
//}
}
else
{ //右孩子
Node *brother = pNode->_left;
//if (brother)
//{ //如果brother存在(黑色节点必定存在兄弟节点)
if (brother->_color == Red)
{ //情况1:兄弟节点的颜色为红色
brother->_color = Black;
Node* parent = brother->_parent;
parent->_color = Red;
_RotateR(parent);
}
else
{
Node *left = brother->_left; //兄弟节点的左孩子
Node *right = brother->_right; //兄弟节点的右孩子

if ((!left && !right) || ((left && left->_color == Black) && (right && right->_color == Black)))
{//情况2:兄弟节点的左右孩子均为黑色或者都不存在
brother->_color = Red;
node = node->_parent;
if (brother->_parent->_color == Black)
{
continue;
}
}
else if ((!left) || left->_color == Black)
{/* 情况3:兄弟节点的左孩子为NULL或者颜色为黑色,
//左孩子为NULL的时候右孩子必定存在,且为红色
//左孩子为黑色时右孩子必定存在且未红色(上面的条件考虑了它们都为黑色的情况)
*/
Node* parent = right->_parent;
right->_color = Black;
parent->_color = Red;
_RotateL(parent);
brother = brother->_parent;
}
//else if( (!right) || right->_color == Black)
//{
if (brother->_left == NULL && brother->_right == NULL && pNode->_left == brother)
{ //这个时候是兄弟节点左右孩子都不存在的情况,直接将父节点颜色置为黑色即可
node->_color = Black;
node = _root;
}
else
{
if (node != _root)
{
brother->_color = node->_parent->_color;
node->_parent->_color = Black;
}
else
{
brother->_color = Black;
}

if (brother->_left)
brother->_left->_color = Black;
Node* parentB = node->_parent;
_RotateR(parentB);
node = _root;
}
//}
}
//}
}
}
}

bool _CheckRBTree(Node *root,int blacknum,int curblacknum)
{ //检查该树是否是一棵合法红黑树
if (root == NULL)
return true;

if (root->_color == Black)
{//黑色节点
++curblacknum;
}
else
{//红色节点
if (root->_parent && root->_parent->_color == Red)
{//不合要求
cout << "连续两个红色节点" << endl;
return false;
}
}

if (root->_left == NULL && root->_right == NULL)
{//叶子结点
if (curblacknum == blacknum)
return true;

return false;
}

return _CheckRBTree(root->_left,blacknum,curblacknum)
&& _CheckRBTree(root->_right,blacknum,curblacknum);
}

void _InOrder(Node *root)
{
if (root)
{
_InOrder(root->_left);
cout << "[" << root->_key << "] ";
//cout << "[" << root->_key << ","<<root->_val<<"] ";
_InOrder(root->_right);
}
}

void _RotateL(Node *&parent)
{ //左单旋
Node *subR = parent->_right;
Node *subRL = subR->_left;

if (subRL)
{ //如果存在
subRL->_parent = parent;
parent->_right = subRL;
}
subR->_parent = parent->_parent;
parent->_parent = subR;
subR->_left = parent;
parent->_right = subRL;
parent = subR;
if (parent->_parent)
{
Node* grandfa = parent->_parent;
if (grandfa->_key > parent->_key)
grandfa->_left = parent;
else
grandfa->_right = parent;
}
else
{
_root = parent;
}
}

void _RotateR(Node *&parent)
{ //右单旋
Node* subL = parent->_left;
Node *subLR = subL->_right;

if (subLR)
{
subLR->_parent = parent;
parent->_left = subLR;
}
subL->_parent = parent->_parent;
parent->_parent = subL;
subL->_right = parent;
parent->_left = subLR;
parent = subL;
if (parent->_parent)
{
Node *grandfa = parent->_parent;
if (grandfa->_key > parent->_key)
grandfa->_left = parent;
else
grandfa->_right = parent;
}
else
{
_root = parent;
}
}

protected:
Node *_root;
};

void TestRedBlackTree()
{
RedBlackTree<int, int> rb;
int array[] = { 4, 19, 0, 17, 10, 12, 8, 11, 18, 1, 6, 16, 7, 14, 9, 3, 13, 2, 5, 15 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
rb.Insert(array[i], 1);
}
rb.InOrder();
cout << endl;
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
cout << endl;
rb.Remove(array[i]);
cout << "Remove " << array[i]<<"!"<< "是否满足红黑树条件:" << rb.IsRight() << endl;
rb.InOrder();
}
}    
      好了,终于完成了。这里说说我为什么要写这篇博客,真的是因为没有搜到和我实现方式一样的博客,有些博客可能相似,但是他的代码让我看的真的很难受,有的全部是大写字母。希望我的这篇博客能够帮助那些刚刚学习红黑树的童鞋们。大家有什么意见也可以给我提出来,谢谢大家的观光!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息