红黑树【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;}
四、红黑树的运用–高效的二叉搜索树
- C++ STL库– map
- Java 库
- linux内核
- 其他一些库
五、红黑树和AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N))
红黑树不追求完全平衡,只要保证最长路径不超过最短路径的2倍即可,所以相对于AVL树要保持高度差不能超过1的高度平衡而言,红黑树降低了旋转的要求,旋转的次数比AVL少,所以性能会优于AVL树,所以实际运用中红黑树更多。
- 红黑树-RBTree
- RBTree红黑树
- RBTree----红黑树
- RBTree(红黑树)
- 红黑树--RBTree
- 红黑树【RBTree】
- 红黑树(RBtree)
- RBTree-红黑树的实现
- 【c++/数据结构】红黑树-RBTree
- 数据结构-红黑树(RBTree)
- 【数据结构】中的红黑树-RBTree
- 红黑树(RBTree)
- RBTree(红黑树)
- 6.Nginx红黑树rbtree
- RBTree——红黑树
- RBTree
- RBTree
- RBTree
- pycharm的一些快捷键
- 获取当前时间前一周、前一月、前一年的时间
- 贝塞尔曲线扫盲
- java中几种基本算法思想
- 【学习摘记】马士兵JDBC技术_课时8-课时9_DML数据操作语言
- 红黑树【RBTree】
- Binder机制
- 简单按钮样式
- 使用Spring JDBCTemplate简化JDBC的操作(二)
- Spring Cloud实现微服务架构学习
- ES6 Promise的概念以及小实例
- 同事知识分享
- 安装Storm集群和Zookeeper集群./zkServer.sh status报错Error contacting service. It is probably not running.
- centos安装docker