红黑树的基本实现

来源:互联网 发布:电钢琴推荐知乎 编辑:程序博客网 时间:2024/05/04 14:40

先前介绍过AVL树,它是一种高度平衡的二叉搜索树,但是它最大的限制是必须满足左子树与右子树高度之差的绝对值不超过1,所以对旋转的要求较高,而红黑树不追求完全平衡,保证最长路径不超过最短路径的2倍即可,相对而言降低了旋转的要求,因此性能就提高了一些,而且它们的插入删除查找的时间复杂度均为O(lgN),所以红黑树在数据结构中运用还是较为广泛的,首先先来了解一下它的性质。

1.根节点是黑色的;

2.每个节点不是黑色就是红色;

3.如果一个节点是红色的,则它的两个子节点是黑色的,即每条路径上不能存在连续的两个红节点,黑节点可以连续出现;

4.对每个节点,从该节点到其所有后代叶节点的路径上,均包含数目相同的黑色节点.

红黑树的插入:

1.树为空时,直接开辟一个节点插入,并将其颜色置为黑;

2.树不为空,先找到要插入的位置,然后插入,再进行调整,直至满足红黑树的特点,调整时分一下几种情况:




与上面分析方法相同,当parent为grandfather的右孩子时:



代码实现:


template<class K,class V>bool RBTree<K,V>::Insert(const K& key,const V& value){if (_root==NULL){_root=new Node(key,value);_root->_col=BLACK;return true;}Node* cur=_root;Node* parent=NULL;while (cur){if (cur->_key < key){parent=cur;cur=cur->_right;}else if (cur->_key > key){parent=cur;cur=cur->_left;}else              //cur->_key==key{return false;}}//找到要插入的空位置cur,进行插入cur=new Node(key,value);if(parent->_key < key){parent->_right=cur;cur->_parent=parent;}else{parent->_left=cur;cur->_parent=parent;}while (cur!=_root && parent->_col==RED)  //保证parent,grandfather均存在{//遇见两个连续红节点开始调整(cur新插的节点默认为红)Node* grandfather=parent->_parent;if (parent==grandfather->_left){Node* uncle=grandfather->_right;if (uncle && uncle->_col==RED){//将grangfather变为红,parent和uncle变为黑,继续向上调grandfather->_col=RED;parent->_col=BLACK;uncle->_col=BLACK;cur=grandfather;            //继续向上调整parent=cur->_parent;}//uncle为黑或不存在else{if (parent->_right==cur){_RotateL(parent);std::swap(parent,cur); }//cur==parent->_left的情况_RotateR(grandfather);grandfather->_col=RED;parent->_col=BLACK;cur=parent;parent=cur->_parent;}}else   //parent==grandfather->_right{Node* uncle=grandfather->_left;if (uncle && uncle->_col==RED){grandfather->_col = RED;  parent->_col = BLACK;  uncle->_col = BLACK;cur=grandfather;parent=cur->_parent;}else //uncle为黑或不存在{if (parent->_left==cur){_RotateR(parent);std::swap(parent,cur);}_RotateL(grandfather);grandfather->_col=RED;parent->_col=BLACK;cur=parent;parent=cur->_parent;}}}_root->_col=BLACK;return true;}



左右旋转:

template<class K,class V>void RBTree<K,V>::_RotateL(Node*& parent){Node* subR=parent->_right;Node* subRL=subR->_left;parent->_right=subRL;if(subRL)subRL->_parent=parent;subR->_left=parent;Node* ppNode=parent->_parent;parent->_parent=subR;if (ppNode==NULL){_root=subR;subR->_parent=NULL;}else{if(ppNode->_left==parent)ppNode->_left=subR;elseppNode->_right=subR;subR->_parent=ppNode;}}template<class K,class V>void RBTree<K,V>::_RotateR(Node*& parent){Node* subL=parent->_left;Node* subLR=subL->_right;parent->_left=subLR;if(subLR)subLR->_parent=parent;subL->_right=parent;Node* ppNode=parent->_parent;   //ppNode保存起初parent的parent    parent->_parent=subL;          //重新链接if (ppNode==NULL){_root=subL;subL->_parent=NULL;}else{if(ppNode->_left==parent)ppNode->_left=subL;elseppNode->_right=subL;subL->_parent=ppNode;}}



我们在AVLTree那一节中,检查平衡是通过平衡因子不超过1,在这里检查这棵树是不是红黑树能不能也根据最长路径不超过最短路径的2倍来检查呢?答案是肯定不行的,因为它还有4条性质在约束。

检查一棵树是不是红黑树,首先先判定根节点是否为黑色的;其次,判定是不是有连续的两个红节点;然后判定每条路径上的黑节点数量是否相等,在这里采取一种方式是先统计出任意一条路径上的黑色节点数量,然后其他路径删的黑节点数量与之比较。


template<class K,class V>bool RBTree<K,V>:: IsRBTree(){if (_root->_col==RED)return false;int BalckCount=0;    //用来统计任意一条路径的黑节点数量作为基准值int k=0;       //需要与BalckCount比较的其他路径的黑节点数量Node* cur=_root;while (cur){if(cur->_col==BLACK){++BalckCount;}cur=cur->_left;}return _IsRBTree(_root,BalckCount,k);}template<class K,class V>bool RBTree<K,V>:: _IsRBTree(Node* root,const int BalckCount,int k){if (root==NULL)   return true;//连续两个红节点则不是红黑树if (root->_col==RED && root->_parent->_col==RED)return false;if(root->_col==BLACK){++k;}if(root->_left==NULL && root->_right==NULL){if (k==BalckCount)return true;elsereturn false;}return _IsRBTree(root->_left,BalckCount,k)&& _IsRBTree(root->_right,BalckCount,k);}


红黑树效率分析:

假设最坏的情况下,如图:


在最短路径上插入一个节点时间复杂度为O(lgN),而在长路径上需要O(2lgN),总体而言时间复杂度为O(lgN).

红黑树这种数据结构应用十分广泛,在多种编程语言中被用作符号表的实现,如:

  • Java中的java.util.TreeMap,java.util.TreeSet
  • C++ STL中的:map,multimap,multiset
  • .NET中的:SortedDictionary,SortedSet 等

完整代码已上传至github:红黑树

0 0