红黑树【RBTree】

来源:互联网 发布:云数据安全解决方案 编辑:程序博客网 时间:2024/05/14 12:18

一、红黑树简介

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。

红黑树是满足下面红黑性质的二叉搜索树
1. 每个节点,不是红色就是黑色的
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个子节点是黑色的
4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?
首先每条路径上的黑色结点向相同,假入一颗树有n个黑色结点,那么这个红黑树的最短路径就是n,最长路径就是2n(由于红色结点不能连续,所以最长路径中,红色结点都是插在两个黑色结点中间,红色结点最多也是有n个,所以最长路径就是最短路径的两倍),所以可以保证最长路径不超过最短路径的两倍;

二、红黑树的插入

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述
这里写图片描述

变化规则详讲:

如上图所示情况,cur为红且存在,p为红且存在,g为黑且存在,u存在且为黑;
首先根据规则,不能出现连续的红色结点,所以讲P变为黑色;然后g结点的右子树多出一个黑色结点,会破坏每条路径黑色结点相同的规则,此时的u为黑色,所以解决办法就是针对g右旋之后,将p调整为根结点,此时以P为根结点的左子树黑色结点没有增加,和全局的每条路径的黑色结点相同,但是通过旋转,g变为了以P为根结点的右子树上的结点,这时,P的右子树的黑色结点增加1,不符合规则,所以将g变为红色;

同理左旋也一样;


这里写图片描述

这里写图片描述

判断一颗树是否为红黑树,判断思路:
1.根结点必须为黑色结点—直接检查
2.不能有连续的红结点 —每个结点和其father不同时为红色
3.每条路径黑色结点数目相同—先求出一个路径的黑色结点作为基准,然后判断其他路径的黑结点数目是否和其相同;采用传值的方式递归判断,每一层不影响上一层的黑色结点统计的数目;

三、完整代码

#pragma once#include<iostream>using namespace std;enum Colour{    RED,//红色    BLACK,//黑色};template<typename K,typename V>struct RBTreeNode{    K _key;//关键字    V _value;//关键字所指的值    RBTreeNode<K,V>*  _left;//左孩子    RBTreeNode<K,V>*  _right;//右孩子    RBTreeNode<K,V>*  _father;//父亲    Colour _col;//颜色    RBTreeNode(const K& key,const V& value)//构造函数        :_key(key)        ,_value(value)        ,_left(NULL)        ,_right(NULL)        ,_father(NULL)        ,_col(RED)//默认添加的元素为红色,不影响该路径的黑色结点的数量    {}};template<typename K,typename V>class RBTree{    typedef  RBTreeNode<K,V>  Node;public:    RBTree()//构造函数        :_root(NULL)    {}    bool Insert(const K& key,const V& value)//插入    {        //1.空树插入节点        if (_root==NULL)        {            _root=new Node(key,value);            //1.1改变根结点的颜色为黑色            _root->_col=BLACK;            //1.2插入完成,直接返回            return true;        }        //2.插入的不是空树        //2.1寻找插入的位置        Node* cur=_root;//遍历红黑树,寻找并记录插入的位置        Node* father;//记录插入位置的父节点        while (cur)        {            if (cur->_key>key)            {                father=cur;                cur=cur->_left;            }            else if (cur->_key < key)            {                father=cur;                cur=cur->_right;            }            else            {                return false;            }        }        Node* node=cur;//记录要插入的位置,方便返回,针对返回pair结构        //2.2寻找到插入的位置,进行插入        cur=new Node(key,value);        if (father->_key>key)        {            father->_left=cur;        }        else        {            father->_right=cur;         }        cur->_father=father;        //2.3插入完成之后,开始判断是否需要调整颜色以及进行旋转        //当father为空,说明更新调整到了根结点,停止;当father的颜色为黑色;插入或者更新不影响上层,直接结束        //进行调整的情况全建立在father存在且为红的情况        while (father&&father->_col==RED)        {            //进入循环的场景是cur为红,父节点为红,祖父节点为黑,所以只需要根结点叔叔节点的是否存在或者颜色            //以及cur和father是各自父节点的左右孩子进行相应的调整            //2.3.1先定义祖父和叔叔节点            //先定义祖父节点            Node* grandfather=father->_father;                  //在定义叔叔节点            Node* uncle=NULL;            //因为不知道叔叔节点是祖父的左孩子还是右孩子,所以需要判断            //叔叔节点为祖父的右孩子            if (father==grandfather->_left)            {                uncle=grandfather->_right;                // 1.叔叔存在,且为红,进行变色处理                if (uncle&&uncle->_col==RED)                {                    //改变颜色                    father->_col=uncle->_col=BLACK;                    grandfather->_col=RED;                    //向上检查调整                    cur=grandfather;                    father=cur->_father;                }                // 2/3.叔叔存在且为黑,或者不存在                else// uncle->_col==BLACK||uncle==NULL                {                    if (father->_right=cur)//双旋                    {                        RotateL(father);                        swap(father,cur);//更正父子关系                    }                        RotateR(grandfather);                     father->_col=BLACK;                     grandfather->_col=RED;                     break;                 }            }            else//father==grandfather->_right            {                uncle=grandfather->_left;                // 1.叔叔存在,且为红,进行变色处理                if (uncle&&uncle->_col==RED)                {                    //改变颜色                    father->_col=uncle->_col=BLACK;                    grandfather->_col=RED;                    //向上检查                    cur=grandfather;                    father=cur->_father;                }                // 2/3.叔叔存在且为黑,或者不存在                else //uncle->_col==BLACK||uncle==NULL                {                    if (father->_left==cur)// 双旋                    {                        RotateR(father);                        swap(father,cur);//更正父子关系                    }                    RotateL(grandfather);                    father->_col=BLACK;                    grandfather->_col=RED;                    break;                }            }        }        _root->_col=BLACK;        return true;    }    void Inorder()//中序遍历    {         _Inorder(_root);    }    //判断一颗树是否为红黑树    //判断思路:1.根结点必须为黑色结点---直接检查    //2.不能有连续的红结点 ---每个结点和其father不同时为红色    //3.每条路径黑色结点数目相同---先求出一个路径的黑色结点作为基准,然后判断其他路径的黑结点数目是否和其相同;    bool  IsBlance()    {        //1.根结点为红,不是红黑树        if (_root&&_root->_col==RED)        {            return false;        }        //2.求出一个路径的黑色结点作为基准值        int k=0;        int num=0;        Node* cur=_root;        while (cur)        {            if (cur->_col==BLACK)            {                ++k;            }            cur=cur->_left;        }        return _CheakColour(_root)&&_CheakNum(_root,k,num);    }private:    //检查颜色--不能出现两个连续红结点    bool _CheakColour(Node* root)    {        //空树是红黑树        if (root==NULL)        {            return true;        }        //两个连续的红结点,不是红黑树        if (root->_col==RED && root->_father->_col==RED)        {            return false;        }        return _CheakColour(root->_left)&&_CheakColour(root->_right);    }    //判断每个路径的黑色结点是否和基准值相等    bool _CheakNum(Node* root,int k,int num)    {        if (root==NULL)        {            return true;        }                    if (root->_col==BLACK)        {            ++num;        }        if (root->_left==NULL&&root->_right==NULL&&num!=k)        {            return false;        }        return _CheakNum(root->_left,k,num)&&_CheakNum(root->_right,k,num);    }    //中序递归打印    void _Inorder(Node* root)    {        if (root==NULL)        {            return ;        }        _Inorder(root->_left);        cout<<root->_key<<"-"<<root->_value<<endl;        _Inorder(root->_right);    }    //左旋    void RotateL(Node* node)    {        Node* SubR=node->_right;        Node* SubRL=SubR->_left;        Node* PPNode=node->_father;        //1.连接node和subRL        node->_right=SubRL;        if (SubRL)        {            SubRL->_father=node;        }        //2.连接PPNode和SubR        if (PPNode==NULL)        {            _root=SubR;            SubR->_father=PPNode;        }        else//PPNode!=NULL        {            if (PPNode->_left==node)            {                PPNode->_left=SubR;            }            else            {                PPNode->_right=SubR;            }            SubR->_father=PPNode;        }        //3.连接带有SubRL的node和SubR        node->_father=SubR;        SubR->_left=node;    }    //右旋    void RotateR(Node* node)    {        Node* SubL=node->_left;        Node* SubLR=SubL->_right;        Node* PPNode=node->_father;        //1.连接node和SubLR        node->_left=SubLR;        if (SubLR)        {            SubLR->_father=node;        }        //2.连接PPNode和SubL        if (PPNode==NULL)        {            _root=SubL;            SubL->_father=PPNode;        }        else        {            if (PPNode->_left==node)            {                PPNode->_left=SubL;            }            else            {                PPNode->_right=SubL;            }            SubL->_father=PPNode;        }        //3.连接带有SubLR的node和SubL        node->_father=SubL;        SubL->_right=node;    }private:   Node* _root;};int main(){    RBTree<int,int> t;    t.Insert(12,12);        cout<<t.IsBlance()<<endl;    t.Insert(6,6);        cout<<t.IsBlance()<<endl;    t.Insert(15,15);        cout<<t.IsBlance()<<endl;    t.Insert(3,3);        cout<<t.IsBlance()<<endl;    t.Insert(13,13);        cout<<t.IsBlance()<<endl;    t.Insert(8,8);        cout<<t.IsBlance()<<endl;    t.Insert(22,22);        cout<<t.IsBlance()<<endl;    t.Insert(5,5);        cout<<t.IsBlance()<<endl;    t.Insert(4,4);        cout<<t.IsBlance()<<endl;    t.Inorder();    return 0;}

四、红黑树的运用–高效的二叉搜索树

  1. C++ STL库– map
  2. Java 库
  3. linux内核
  4. 其他一些库

五、红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树增删查改的时间复杂度都是O(lg(N))

红黑树不追求完全平衡,只要保证最长路径不超过最短路径的2倍即可,所以相对于AVL树要保持高度差不能超过1的高度平衡而言,红黑树降低了旋转的要求,旋转的次数比AVL少,所以性能会优于AVL树,所以实际运用中红黑树更多。

原创粉丝点击