转载注明出处:http://blog.csdn.net/mxway/article/details/29216199

本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进行调整的过程进行的解释。

最近在看stl源码剖析,看到map底层红黑树的实现。为了加深对于红黑树的理解就自己动手写了红黑树插入的实现。关于红黑树插入节点后破坏红黑树性质的几种情况,可以在网上搜到很多相关的信息。下面用图说明插入新节点时红黑树所做的调整。插入的序列分别是30,40,50,20,35,10,11。

首先是插入30,由于现在红黑树是一棵空,所以直接将30作为根节点,并将根节点颜色染成黑色即可。

一、插入40,如下图所示。由于此时并没有破坏红黑树的性质,所以此时插入成功。

图1:插入40

二、插入50,插入节点50后,50及其父节点都为红色,由于此时40没有兄弟节点。此时将父节点变黑,祖父节点变红,以祖父节点为根进行左旋。调整后如图2-2所示

图2-1:插入节点50                                                                  图2-2:插入节点50调整后

三、插入20,插入节点20后,其父节点30及叔节点50都是红色节点。解决方法是将父节点及叔节点变黑,祖父节点变红。祖父节点变成新的当前节点重新进行算法。由于40是根节点,所以将根节点变黑。调整后如图3-2所示,此时满足红黑树的全部性质。

图3-1:插入节点20                                                                 图3-2:插入节点20调整后

四、插入节点35,如图4所示,由于此时并未破坏红黑树的任何性质。不需要进行调整算法。

图4:插入节点35

五、插入节点10,此时10的父节点20及其叔节点35都是红色。将20及35节点变黑,祖父节点30变红。30作为新的当前节点。由于30的父节点40是黑色。所以调整算法结束。调整后如图5-2所示

图5-1:插入节点10

图5-2:插入节点10调整后

六、插入节点11,如图6-1所示,由于11的父节点10是红色节点,并且11是10的右子节点。所以首先以10节点为根节点进行左旋,旋转后其的红黑树如图6-2所示。此时节点10的父节点11为红色,并且10是11节点的左子节点;此时将父节点11变黑,祖父节点20变红,以祖父节点20为根节点进行右旋。旋转后如图6-3所示。

图6-1:插入节点11

图6-2:10为根节点进行左旋后

图6-3:插入节点调整后的红黑树

下面附上只实现了插入功能的红黑树源码

#include <iostream>
#include <queue>
using namespace std;

static int _rb_black_node = 0;
static int _rb_red_node   = 1;
template<typename T>
struct RBNode
{
    RBNode():left(NULL),right(NULL),parent(NULL),val(T()),color(_rb_red_node){}
    RBNode(const T &v1):left(NULL),right(NULL),parent(NULL),val(v1),color(_rb_red_node){}
    RBNode  *left;
    RBNode  *right;
    RBNode  *parent;
    int     color;
    T       val;
};

template<typename T>
class RBTree
{
public:
    RBTree():root(NULL){}
    ~RBTree()
    {
        if(root)
        {
            Destroy(root);
        }
    }
    void    print();
    void    Search(const T &v1, RBNode<T> *&node);
    bool    InsertUnique(const T &v1);
    void    DeleteValue(const T &v1);
    void    Destroy(RBNode<T> *p);
    void    InsertReBalance(RBNode<T> *node);
    RBNode<T>*    _rbtree_rotate_left(RBNode<T> *node);
    RBNode<T>*  _rbtree_rotate_right(RBNode<T> *node);
private:
    RBNode<T> *root;
};

/*
*
* 打印红黑树的节点信息
*
*/
template<typename T>
void RBTree<T>::print()
{
    RBNode<T> *p;
    queue<RBNode<T> *> Q;
    Q.push(root);
    while(!Q.empty())
    {
        p = Q.front();
        Q.pop();
        cout<<"节点: "<<p->val<<" ";
        if(p->left)
        {
            cout<<"left:"<<p->left->val<<"->color:"<<p->left->color<<" ";
            Q.push(p->left);
        }
        if(p->right)
        {
            cout<<"right:"<<p->right->val<<"->color:"<<p->right->color<<" ";
            Q.push(p->right);
        }
        cout<<endl<<endl;
    }
}

/*
*
* 搜索v1在红黑树中出现的位置,如果v1在红黑树中则node节点为
* 值为v1所在红黑树中的节点。
* 否则node节点为如果将v1插入到红黑树中的父节点
*
*/
template<typename T>
void RBTree<T>::Search(const T &v1,RBNode<T> *&node)
{
    RBNode<T> *p = root;
    node = NULL;
    while(p)
    {
        if(p->val == v1)
        {
            node = p;
            break;
        }
        else if(p->val < v1)
        {
            node = p;
            p = p->right;
        }
        else
        {
            node = p;
            p = p->left;
        }
    }
}

template<typename T>
bool RBTree<T>::InsertUnique(const T &v1)
{
    RBNode<T> *parent = NULL;
    RBNode<T> *newNode = new RBNode<T>(v1);
    Search(v1, parent);
    if(parent == NULL)
    {//红黑树为空,当前插入的节点为根节点。插入后将根颜色变为黑
        root = newNode;
        root->color = _rb_black_node;
        return true;
    }
    if(parent->val == v1)//v1已经存在红黑树中。不再插入
        return false;

    if(v1 < parent->val)
    {
        parent->left = newNode;
    }
    else
    {
        parent->right = newNode;
    }
    newNode->parent = parent;
    InsertReBalance(newNode);
    return true;
}

/*
*
* 插入节点后进行调整,
* 使所有节点满足红黑树的性质
*
*/
template<typename T>
void RBTree<T>::InsertReBalance(RBNode<T> *node)
{
    RBNode<T> *parent = node->parent;
    RBNode<T> *grandParent = NULL;
    while(parent && parent->color==_rb_red_node)
    {
        grandParent = parent->parent;
        if(parent == grandParent->left)
        {//父节点为祖父节点的左儿子
            RBNode<T> *uncle = grandParent->right;
            if(uncle && uncle->color == _rb_red_node)
            {//情形1 父节点与叔节点都为红
                //解决方法父与叔变黑,祖父变黑。祖父变为新的当前节点重新进入算法
                parent->color = _rb_black_node;
                uncle->color  = _rb_black_node;
                grandParent->color = _rb_red_node;
                node = grandParent;
                parent = grandParent->parent;
            }
            else
            {
                if(node == parent->right)
                {//情形2,叔为黑,当前节点为其父节点的右子节点
                    //解决方法:以父节点为根进行左旋
                    //操作后将转换为情形3
                    node = _rbtree_rotate_left(parent);
                    parent = node->parent;
                    grandParent = parent->parent;
                }
                //情形3父为红,当前节点为父节点的左子节点
                //解决方法:父节点变黑,祖父节点变红,以
                //祖父节点为根节点进行右旋
                parent->color = _rb_black_node;
                grandParent->color = _rb_red_node;
                _rbtree_rotate_right(grandParent);
            }
        }
        else
        {//父节点为祖父节点的右子节点,情况与上面相同
            RBNode<T> *uncle = grandParent->left;
            if(uncle && uncle->color == _rb_red_node)
            {
                uncle->color = _rb_black_node;
                parent->color = _rb_black_node;
                grandParent->color = _rb_red_node;
                node = grandParent;
                parent = node->parent;
            }
            else
            {
                if(node == parent->left)
                {
                    node = _rbtree_rotate_right(parent);
                    parent = node->parent;
                    grandParent = parent->parent;
                }
                parent->color = _rb_black_node;
                grandParent->color = _rb_red_node;
                _rbtree_rotate_left(grandParent);
            }
        }
    }
    root->color = _rb_black_node;
}

/*
*
* 左旋
*
*/
template<typename T>
RBNode<T> *RBTree<T>::_rbtree_rotate_left(RBNode<T> *x)
{
    RBNode<T> *y = x->right;
    if(y == NULL)
    {
        return x;
    }
    //x的右节点为y的左节点
    x->right = y->left;
    if(y->left)//如果y的左节点存在,其父节点为y
        y->left->parent = x;
    if(root == x)
    {//x为root,旋转后y为新的root根节点
        root = y;
    }
    else
    {
        if(x == x->parent->left)
        {//如果x为其父节点的左子节点。
            //x的父节点的新左子节点为y
            x->parent->left = y;
        }
        else
        {
            x->parent->right = y;
        }
        //y的父节点为x的父节点
        y->parent = x->parent;
    }
    //y的左子节点为x
    y->left = x;
    //x的父节点为y
    x->parent = y;
    return x;
}

/*
*
* 右旋
* 分析其逻辑与左旋代码类似
*
*/
template<typename T>
RBNode<T>* RBTree<T>::_rbtree_rotate_right(RBNode<T> *x)
{
    RBNode<T> *y = x->left;
    if(y == NULL)
    {
        return x;
    }
    x->left = y->right;
    if(y->right)
        y->right->parent = x;
    if(root == x)
    {
        root = y;
    }
    else
    {
        if(x == x->parent->left)
        {
            x->parent->left = y;
        }
        else
        {
            x->parent->right = y;
        }
        y->parent = x->parent;
    }
    y->right = x;
    x->parent = y;
    return x;
}

/*
*
* 销毁整棵红黑树
*
*/
template<typename T>
void RBTree<T>::Destroy(RBNode<T> *p)
{
    if(p->left)
    {
        Destroy(p->left);
    }
    if(p->right)
    {
        Destroy(p->right);
    }
    delete p;
}

int main()
{
    RBTree<int> obj;
    obj.InsertUnique(30);
    obj.InsertUnique(40);
    obj.InsertUnique(50);
    obj.InsertUnique(20);
    obj.InsertUnique(35);
    obj.InsertUnique(10);
    obj.InsertUnique(11);
    obj.print();
    return 0;
}

stl map底层之红黑树插入步骤详解与代码实现的更多相关文章

  1. 红黑树的删除详解与思路分析——不同于教科书上的算法(dart语言实现)

    对于红黑树的删除,看了数据结构的书,也看了很多网上的讲解和实现,但都不满意.很多讲解都是囫囵吞枣,知其然,不知其所以然,讲的晦涩难懂. 红黑树是平衡二叉树的一种,其删除算法是比较复杂的,因为删除后还要 ...

  2. Thrift实现C#调用Java开发步骤详解

    概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...

  3. STL之map与pair与unordered_map常用函数详解

    STL之map与pair与unordered_map常用函数详解 一.map的概述 map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称 ...

  4. RB-Tree插入过程详解

    红黑树具有很优秀的特性,其自平衡性特性,局部调整特性使得红黑树插入,删除,以查找,以及这些过程的内存资源的占用,的综合性能是非常高的(通常我们会拿红黑树和AVL树进行对比). 对于红黑树的这些特性,在 ...

  5. gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解

    摘自http://blog.csdn.net/elfprincexu/article/details/45043971 gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解 C和C+ ...

  6. sql server 2008 数据库管理系统使用SQL语句创建登录用户步骤详解

    介绍了sql server 2008 数据库管理系统使用SQL语句创建登录用户步骤详解 --服务器角色: --固定服务器角色具有一组固定的权限,并且适用于整个服务器范围. 它们专门用于管理 SQL S ...

  7. 人工智能之深度学习-初始环境搭建(安装Anaconda3和TensorFlow2步骤详解)

    前言: 本篇文章主要讲解的是在学习人工智能之深度学习时所学到的知识和需要的环境配置(安装Anaconda3和TensorFlow2步骤详解),以及个人的心得体会,汇集成本篇文章,作为自己深度学习的总结 ...

  8. ASP.NET连接Oracle数据库的步骤详解(转)

    ASP.NET连接Oracle数据库的步骤详解   本文我们主要介绍了ASP.NET连接Oracle数据库的步骤及每个步骤需要进行的设置,希望能够对您有所帮助.   在用ASP.NET开发应用程序时, ...

  9. Oracle 11g客户端在Linux系统上的配置步骤详解

    Oracle 11g客户端在Linux系统上的配置步骤详解 2011-07-26 10:47 newhappy2008 CSDN博客 字号:T | T 本文我们主要介绍了Oracle 11g客户端在L ...

随机推荐

  1. jQuery的delegate()与proxy()方法

    1. jQuery 事件 - delegate() 方法 定义和用法 delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数. 使用 ...

  2. c++ DISALLOW_COPY_AND_ASSIGN

    Google C++编程规范 – 第三十二条 -<拷贝构造函数> [规范] 仅在确认需要的时候,才定义拷贝构造函数和赋值运算符:否则,请使用DISALLOW_COPY_AND_ASSIGN ...

  3. linux 内核模块ko入门

    http://blog.csdn.net/elfylin/article/details/5908265

  4. G面经prepare: Maximum Subsequence in Another String's Order

    求string str1中含有string str2 order的 subsequence 的最小长度 DP做法:dp[i][j]定义为pattern对应到i位置,string对应到j位置时,shor ...

  5. 转:SELENIUM TIPS: CSS SELECTORS

    This page will show you some CSS rules and pseudo-classes that will help you move your XPATH locator ...

  6. UML:组件图

    要搞清楚组件图,必须先搞清楚什么是组件? 组件有以下特点:1.能实现一定功能,或者提供一些服务.2.不能单独运行,要作为系统的一部分来发挥作用.3.在物理上独立的,不是逻辑上的概念.4.可单独维护.可 ...

  7. const修饰

    const int A() //const // ====>int A(const this) { //观点1:const是修饰a,但是通过测试,我们发现,b++也不能编译通过 //这说明:co ...

  8. Android使用ZXing生成带图片的二维码

    效果图如下: 制作过程很简单的就是在原始的二维码图片上添加一个logn图标,代码的注释写得很详细,也就不给大家啰嗦了 package com.example.day44_02_qrcodewithlo ...

  9. Linux 远程和本地的一些解决方案

     有的小伙伴想Linux 远程登录 两台机器同时root登录,其实可以同时多个用户的. Linux是多用户的多任务系统,可以同时多个用户登录到系统,也可以一个用户通过不同终端登录到一个系统执行不同的操 ...

  10. paper 85:机器统计学习方法——CART, Bagging, Random Forest, Boosting

    本文从统计学角度讲解了CART(Classification And Regression Tree), Bagging(bootstrap aggregation), Random Forest B ...