用C++来实现红黑树完整代码

黑格尔曾经说过:熟知非真知,一直在使用map和set容器,也知道它们的底层是红黑树,但是红黑树究竟是如何实现的?今天剖析了一下红黑树的底层原理,并且动手实现了一棵红黑树
首先,红黑树的几个性质如下:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3. 所有叶子都是黑色。(叶子是NULL节点!
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

这个红黑树具有这样几个API接口:

  • 1、判断节点的红、黑
  • 2、在树上插入节点
  • 3、根据红黑树结点的key值找到value的值
  • 4、判断红黑树上元素的个数
  • 5、层序遍历打印红黑树(有换行型)
    在实现API接口前,需要对红黑树的结点定义一个结构体:
template <class T1, class T2>
class RBnode {
public:
    bool color;
    T1 key;
    T2 vaule;
    RBnode *left;
    RBnode *right;
    RBnode(T1 key, T2 vaule,bool c, RBnode *l, RBnode *r){
        this->key = key;
        this->vaule = vaule;
        color = c;
        left = l;
        right = r;
    }
};

这部分直接以代码的形式给出,使用类模板T1,T2,每一个结点包含属性为:key值、value值、颜色信息(红1黑0)、左右子节点。除此之外,在红黑树的类中要定义的成员变量有:元素的个数、根节点的信息、左旋右旋颜色翻转的方法:

左旋

右旋

颜色翻转

这三种方法是为了维护红黑树插入删除时规则而制定的翻转规则,需要保证红黑树的只有左侧能为红、同一节点不能连接两个红色节点、还有红黑树本身具有的性质:1、平衡树。2、查找树:左value<根value<右value的性质而制定的,在实际的插入过程中,规则如下:


红黑树插入结点时的规则

红黑树插入结点时的规则

OK!具备了实现红黑树的基础知识和API设计之后我们来完成对红黑树的实际即可,下面直接上完整的红黑树实现以及测试的代码(注意模板的嵌套使用):
#include <iostream>
using namespace std;
#include <string>
#include <queue>
template <class T1, class T2>
class RBnode {
public:
    bool color;
    T1 key;
    T2 vaule;
    RBnode *left;
    RBnode *right;
    RBnode(T1 key, T2 vaule,bool c, RBnode *l, RBnode *r){
        this->key = key;
        this->vaule = vaule;
        color = c;
        left = l;
        right = r;
    }
};
template <class T1,class T2>
class RBtree
{
public:
    RBnode<T1,T2> *root; //根节点
private:
    int N;           //树中的元素个数
    bool red = true;    
    bool black = false;


    //成员方法2:左旋
    RBnode<T1,T2>* rotate_left(RBnode<T1, T2> *x) {
        RBnode<T1, T2> *x_r = x->right;
        RBnode<T1, T2> *x_r_l = x_r->left;
        x->right = x_r_l;
        x_r->left = x;
        x_r->color = x->color;
        x->color = red;
        return x_r;
    }
    //成员方法3:右旋
    RBnode<T1, T2>* rotate_right(RBnode<T1, T2> *x) {
        RBnode<T1, T2> *x_l = x->left;
        RBnode<T1, T2> *x_r = x->right;
        RBnode<T1, T2> *x_l_r = x_l->right;
        x->left = x_l_r;
        x_l->right = x;
        x_l->color = x->color;
        x->color = red;
        return x_l;
    }
    //成员方法3: 颜色翻转
    RBnode<T1, T2>* flip_color(RBnode<T1, T2> *x) {
        x->color = red;
        x->left->color = black;
        x->right->color = black;
        return x;
    }
public:
    //成员方法1:判断节点是否为红色节点
    bool is_red(RBnode<T1, T2> *x) {
        if (x == NULL) return false; //空节点为黑色!!
        else return x->color == red;
    }
    //成员方法4: 整个树上插入
    void put(T1 key, T2 vaule) {
        root = put(root, key, vaule);
        root->color = black;
    }
    //成员方法5: 指定树插入
    RBnode<T1, T2>* put(RBnode<T1, T2> *x, T1 key, T2 vaule) {
        if (x == NULL) {
            N++;
            RBnode<T1, T2>* k = new RBnode<T1, T2> (key, vaule, red, NULL, NULL);
            return k;
        }
        if (key > x->key) x->right = put(x->right, key, vaule);
        else if (key < x->key) x->left = put(x->left, key, vaule);
        else x->vaule = vaule; //若key值相同,则更新相当节点的value值
        if (is_red(x->right) && !is_red(x->left)) x=rotate_left(x);
        if (is_red(x->left) && is_red(x->left->left)) x=rotate_right(x);
        if (is_red(x->right) && is_red(x->left)) x=flip_color(x);
        return x;
    }
    //成员方法6:根据key查找对应的值
    T2 get(T1 key) {
        return get(root, key);
    }
    T2 get(RBnode<T1, T2> *x, T1 key) {
        if (x == NULL) return NULL;
        if (key < x->key) {
            return get(x->left, key);
        }
        else if (key > x->key) {
            return get(x->right, key);
        }
        else return x->vaule;
    }
    //成员方法7:获取树中元素个数
    int size() {
        return N;
    }
public:
    void print_it(RBnode<T1, T2>*x) {
        if (x == NULL) return;
        queue<RBnode<T1, T2>*> res;
        res.push(x);
        int to_print = 1;
        int next_line = 0;
        while (res.size() != 0) {
            RBnode<T1, T2>*p = res.front();
            if (p->left != NULL) {
                res.push(p->left);
                ++next_line;
            }
            if (p->right != NULL) {
                res.push(p->right);
                ++next_line;
            }
            if (p->color)
                cout << p->key << " " << p->vaule << " " << "红色" << "       ";
            else cout << p->key << " " << p->vaule << " " << "黑色" << "       ";
            --to_print;
            res.pop();
            if (to_print == 0) {
                to_print = next_line;
                cout << endl;
                next_line = 0;
            }
        }
    }
};

int main() {
    RBtree<int, string> res ;
    res.put(3, "TJ");
    res.put(4, "NUK");
    res.put(1, "TJU");
    res.print_it(res.root);
    res.put(5, "自动化");
    res.put(2, "国工");
    cout << res.get(5) << endl;
    
    res.put(5, "湖人总冠军");
    cout << res.get(5) << endl;
    res.put(6, "0offer");
    res.put(9, "bboffer");
    cout << res.size();
    cout << endl;
    res.print_it(res.root);
    system("pause");
}

我们对每个红黑树的节点的key赋为int类型,value为string类型,我们打印一下测试结果:


红黑树的测试结果

我们将这个结果画一下显得更加直观:


测试结果手绘

我们可以看出,构造的二叉树完全符合红黑树的条件,但是要注意:红黑树虽然称他是平衡树,但是并不一定满足各个同一深度的各个子树深度差至多为1的条件,它是依靠自身的性质来自平衡。到此,一棵简易的红黑树就搭建完毕啦。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352