C++boolan part3_week3

由于对红黑树理解不深,课后对红黑树进行了较深入的探索。

此笔记主要对红黑树进行归纳理解,其中不免参照网上资料

红黑树的简介

红黑树和以前学过的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。据说是红黑树有更好的效率,更高的统计性能。
红黑树和AVL树的区别在于它使用颜色来标识结点的高度,它所追求的是局部平衡而不是AVL树中的非常严格的平衡。学过数据结构的人应该都已经领教过AVL树的复杂,但AVL树的复杂比起红黑树来说简直是小巫见大巫,红黑树才是真正的变态级数据结构。
由于STL中的关联式容器默认的底层实现都是红黑树,因此红黑树对于学习STL源码还是很重要的,有必要掌握红黑树的实现原理和源码实现。
红黑树是AVL树的变种,红黑树通过一些着色法则确保没有一条路径会比其它路径长出两倍,因而达到接近平衡的目的。所谓红黑树,不仅是一个二叉搜索树,而且必须满足一下规则:
1、每个节点不是红色就是黑色。
2、根节点为黑色。
3、如果节点为红色,其子节点必须为黑色。
4、任意一个节点到到NULL(树尾端)的任何路径,所含之黑色节点数必须相同。
上面的这些约束保证了这个树大致上是平衡的,这也决定了红黑树的插入、删除、查询等操作是比较快速的。 根据规则4,新增节点必须为红色;根据规则3,新增节点之父节点必须为黑色。当新增节点根据二叉搜索树的规则到达其插入点时,却未能符合上述条件时,就必须调整颜色并旋转树形,如下图:


红黑树的插入和删除



// 元素插入操作  insert_unique()  
// 插入新值:节点键值不允许重复,若重复则插入无效  
// 注意,返回值是个pair,第一个元素是个红黑树迭代器,指向新增节点  
// 第二个元素表示插入成功与否  
template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>  
pair<typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator , bool>  
rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::insert_unique(const Value &v)  
{  
    rb_tree_node* y = header;    // 根节点root的父节点  
    rb_tree_node* x = root();    // 从根节点开始  
    bool comp = true;  
    while(x != 0)  
    {  
        y = x;  
        comp = key_compare(KeyOfValue()(v) , key(x));    // v键值小于目前节点之键值?  
        x = comp ? left(x) : right(x);   // 遇“大”则往左,遇“小于或等于”则往右  
    }  
    // 离开while循环之后,y所指即插入点之父节点(此时的它必为叶节点)  
    iterator j = iterator(y);     // 令迭代器j指向插入点之父节点y  
    if(comp)     // 如果离开while循环时comp为真(表示遇“大”,将插入于左侧)  
    {  
        if(j == begin())    // 如果插入点之父节点为最左节点  
            return pair<iterator , bool>(_insert(x , y , z) , true);  
        else     // 否则(插入点之父节点不为最左节点)  
            --j;   // 调整j,回头准备测试  
    }  
    if(key_compare(key(j.node) , KeyOfValue()(v) ))  
        // 新键值不与既有节点之键值重复,于是以下执行安插操作  
        return pair<iterator , bool>(_insert(x , y , z) , true);  
    // 以上,x为新值插入点,y为插入点之父节点,v为新值  
  
    // 进行至此,表示新值一定与树中键值重复,那么就不应该插入新值  
    return pair<iterator , bool>(j , false);  
}  
  
// 真正地插入执行程序 _insert()  
template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>  
typename<Key , Value , KeyOfValue , Compare , Alloc>::_insert(base_ptr x_ , base_ptr y_ , const Value &v)  
{  
    // 参数x_ 为新值插入点,参数y_为插入点之父节点,参数v为新值  
    link_type x = (link_type) x_;  
    link_type y = (link_type) y_;  
    link_type z;  
  
    // key_compare 是键值大小比较准则。应该会是个function object  
    if(y == header || x != 0 || key_compare(KeyOfValue()(v) , key(y) ))  
    {  
        z = create_node(v);    // 产生一个新节点  
        left(y) = z;           // 这使得当y即为header时,leftmost() = z  
        if(y == header)  
        {  
            root() = z;  
            rightmost() = z;  
        }  
        else if(y == leftmost())     // 如果y为最左节点  
            leftmost() = z;          // 维护leftmost(),使它永远指向最左节点  
    }  
    else  
    {  
        z = create_node(v);        // 产生一个新节点  
        right(y) = z;              // 令新节点成为插入点之父节点y的右子节点  
        if(y == rightmost())  
            rightmost() = z;       // 维护rightmost(),使它永远指向最右节点  
    }  
    parent(z) = y;      // 设定新节点的父节点  
    left(z) = 0;        // 设定新节点的左子节点  
    right(z) = 0;       // 设定新节点的右子节点  
    // 新节点的颜色将在_rb_tree_rebalance()设定(并调整)  
    _rb_tree_rebalance(z , header->parent);      // 参数一为新增节点,参数二为根节点root  
    ++node_count;       // 节点数累加  
    return iterator(z);  // 返回一个迭代器,指向新增节点  
}  
  
  
// 全局函数  
// 重新令树形平衡(改变颜色及旋转树形)  
// 参数一为新增节点,参数二为根节点root  
inline void _rb_tree_rebalance(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
{  
    x->color = _rb_tree_red;    //新节点必为红  
    while(x != root && x->parent->color == _rb_tree_red)    // 父节点为红  
    {  
        if(x->parent == x->parent->parent->left)      // 父节点为祖父节点之左子节点  
        {  
            _rb_tree_node_base* y = x->parent->parent->right;    // 令y为伯父节点  
            if(y && y->color == _rb_tree_red)    // 伯父节点存在,且为红  
            {  
                x->parent->color = _rb_tree_black;           // 更改父节点为黑色  
                y->color = _rb_tree_black;                   // 更改伯父节点为黑色  
                x->parent->parent->color = _rb_tree_red;     // 更改祖父节点为红色  
                x = x->parent->parent;  
            }  
            else    // 无伯父节点,或伯父节点为黑色  
            {  
                if(x == x->parent->right)   // 如果新节点为父节点之右子节点  
                {  
                    x = x->parent;  
                    _rb_tree_rotate_left(x , root);    // 第一个参数为左旋点  
                }  
                x->parent->color = _rb_tree_black;     // 改变颜色  
                x->parent->parent->color = _rb_tree_red;  
                _rb_tree_rotate_right(x->parent->parent , root);    // 第一个参数为右旋点  
            }  
        }  
        else          // 父节点为祖父节点之右子节点  
        {  
            _rb_tree_node_base* y = x->parent->parent->left;    // 令y为伯父节点  
            if(y && y->color == _rb_tree_red)    // 有伯父节点,且为红  
            {  
                x->parent->color = _rb_tree_black;           // 更改父节点为黑色  
                y->color = _rb_tree_black;                   // 更改伯父节点为黑色  
                x->parent->parent->color = _rb_tree_red;     // 更改祖父节点为红色  
                x = x->parent->parent;          // 准备继续往上层检查  
            }  
            else    // 无伯父节点,或伯父节点为黑色  
            {  
                if(x == x->parent->left)        // 如果新节点为父节点之左子节点  
                {  
                    x = x->parent;  
                    _rb_tree_rotate_right(x , root);    // 第一个参数为右旋点  
                }  
                x->parent->color = _rb_tree_black;     // 改变颜色  
                x->parent->parent->color = _rb_tree_red;  
                _rb_tree_rotate_left(x->parent->parent , root);    // 第一个参数为左旋点  
            }  
        }  
    }//while  
    root->color = _rb_tree_black;    // 根节点永远为黑色  
}  
  
  
// 左旋函数  
inline void _rb_tree_rotate_left(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
{  
    // x 为旋转点  
    _rb_tree_node_base* y = x->right;          // 令y为旋转点的右子节点  
    x->right = y->left;  
    if(y->left != 0)  
        y->left->parent = x;           // 别忘了回马枪设定父节点  
    y->parent = x->parent;  
  
    // 令y完全顶替x的地位(必须将x对其父节点的关系完全接收过来)  
    if(x == root)    // x为根节点  
        root = y;  
    else if(x == x->parent->left)         // x为其父节点的左子节点  
        x->parent->left = y;  
    else                                  // x为其父节点的右子节点  
        x->parent->right = y;  
    y->left = x;  
    x->parent = y;  
}  
  
  
// 右旋函数  
inline void _rb_tree_rotate_right(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
{  
    // x 为旋转点  
    _rb_tree_node_base* y = x->left;          // 令y为旋转点的左子节点  
    x->left = y->right;  
    if(y->right != 0)  
        y->right->parent = x;           // 别忘了回马枪设定父节点  
    y->parent = x->parent;  
  
    // 令y完全顶替x的地位(必须将x对其父节点的关系完全接收过来)  
    if(x == root)  
        root = y;  
    else if(x == x->parent->right)         // x为其父节点的右子节点  
        x->parent->right = y;  
    else                                  // x为其父节点的左子节点  
        x->parent->left = y;  
    y->right = x;  
    x->parent = y;  
} 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,373评论 1 31
  • 树(tree)的基本知识 一.定义 树是一种抽象数据类型,或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构...
    337b94dc718f阅读 7,153评论 3 42
  • TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做...
    Android看海阅读 1,139评论 0 4
  • Java关于数据结构的实现:树 关于作者 郭孝星,程序员,吉他手,主要从事Android平台基础架构方面的工作,欢...
    郭孝星阅读 2,884评论 2 4
  • 不知道为什么看日出最好的地方是山顶,那么多人都错过繁星去拥抱太阳。如果可以的话,为什么不在夜晚爬山的路上,观望那片...
    省钱君阅读 262评论 0 1