【数据结构】向STL看齐-模拟实现红黑树
2017-05-16 15:11
302 查看
目标:回顾模板分离编译,掌握平衡树的左旋和右旋,掌握红黑树插入过程调整逻辑,即旋转和变色。
红黑树的特点:
1、任意一个结点要么是红色,要么是黑色
2、根结点是黑色
3、一条路径上不能出现两个连续的红色结点
4、从根节点到任一叶子节点的黑色结点个数相同
5、NUL结点默认为黑色结点(可忽略)
使用模板分离编译实现红黑树的基本操作:拷贝构造,赋值重载,插入、判空、查找key是否存在、个数、中序打印等。
RBTree.hpp
重点是插入过程的调整
在分析或画图之前要牢记:1.cur为当前结点 2.p为父节点 3.g为祖父结点 4.u为叔叔结点
一、当pCur为根节点时无需旋转调整,直接让根节点的颜色为黑即可,无需进入循环
二、当pCur不为根节点时,调整出现两个红色结点相连的情况,注意此时cur一定是红,p也是红,g为黑,可分为如下情况:
a). 叔叔结点存在且为红色, 此时将父亲和叔叔结点调整为黑色,祖父变为红色,然后继续循环
b). 叔叔结点不存在或者为黑色,即 a)的 else情况,主要有如下情形:
1.p是g的左孩子
如果cur是p的左孩子 , 需要右旋g --- 简称 左左 右旋
如果cur是p的右孩子 , 先左旋p, 然后和上述<1>.中第一条情况一样 --- 简称 左右,先左旋后右旋
2. p是g的右孩子
如果cur是p右孩子 , 需要左旋g --- 简称 右右 左旋
如果cur是p的左孩子 , 先右旋p, 然后和上述<2>. 中第一条情况一样 --- 简称 右左,先右旋后左旋
完成后将 g 的颜色改为红色 p的颜色改为黑色
在test.cpp中有测试用例,基本覆盖了上面的所有情况。
RBTree.cpp
test.cpp
红黑树的特点:
1、任意一个结点要么是红色,要么是黑色
2、根结点是黑色
3、一条路径上不能出现两个连续的红色结点
4、从根节点到任一叶子节点的黑色结点个数相同
5、NUL结点默认为黑色结点(可忽略)
使用模板分离编译实现红黑树的基本操作:拷贝构造,赋值重载,插入、判空、查找key是否存在、个数、中序打印等。
RBTree.hpp
#ifndef _RBTREE_H_ #define _RBTREE_H_ #include <iostream> using namespace std; // 标识红黑树结点颜色 enum Color{RED, BLACK}; // 红黑树结点类型 template<class K, class V> struct RBTreeNode { RBTreeNode<K, V>* _left; // 左指针域 RBTreeNode<K, V>* _right; // 右指针域 RBTreeNode<K, V>* _parent; // 指向父亲 K _key; // 结点的key值 V _val; // 结点对应的value Color _color; // 结点的颜色 // 构造函数 RBTreeNode(const K &key, const V &val, Color color = RED) : _left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _val(val) , _color(color) {} }; // 红黑树 template<class K, class V> class RBTree { public: typedef RBTreeNode<K, V> Node; public: RBTree() :_root(NULL) , _size(0) {} explicit RBTree(const RBTree & rb); // 拷贝构造 RBTree& operator= (const RBTree &rb); // 赋值运算符重载 bool Insert(const K &key, const V &val);// 红黑树的插入 bool IsRBTree(); // 是否是红黑树 void Inorder(); // 中序打印 bool Find(const K& key); // 查找key是否存在 size_t Size(); // 结点个数 bool IsEmpty(); // 判断是否为空 ~RBTree(); // 析构函数 private: Node* _Copy(const Node* pNode, Node* parent); // 拷贝一棵树 void _Inorder(Node *pNode); // 递归中序遍历 void RotateLeft(Node* parent); // 左旋 void RotateRight(Node* parent); // 右旋 void _Clear(Node*& pNode); // 清空 // 判断是不是红黑树 bool _IsRBTree(Node* pNode, const size_t blackCount, size_t curBlackCount); bool _Find(Node* pNode, const K& key); private: Node* _root; // 根节点 size_t _size; // 结点个数 }; #endif /* _RBTREE_H_ */
重点是插入过程的调整
在分析或画图之前要牢记:1.cur为当前结点 2.p为父节点 3.g为祖父结点 4.u为叔叔结点
一、当pCur为根节点时无需旋转调整,直接让根节点的颜色为黑即可,无需进入循环
a). 叔叔结点存在且为红色, 此时将父亲和叔叔结点调整为黑色,祖父变为红色,然后继续循环
b). 叔叔结点不存在或者为黑色,即 a)的 else情况,主要有如下情形:
1.p是g的左孩子
如果cur是p的左孩子 , 需要右旋g --- 简称 左左 右旋
如果cur是p的右孩子 , 先左旋p, 然后和上述<1>.中第一条情况一样 --- 简称 左右,先左旋后右旋
2. p是g的右孩子
如果cur是p右孩子 , 需要左旋g --- 简称 右右 左旋
如果cur是p的左孩子 , 先右旋p, 然后和上述<2>. 中第一条情况一样 --- 简称 右左,先右旋后左旋
完成后将 g 的颜色改为红色 p的颜色改为黑色
在test.cpp中有测试用例,基本覆盖了上面的所有情况。
RBTree.cpp
#include "RBTree.hpp" /********************/ /* 以下为 public函数*/ /*******************/ // 拷贝构造 template<class K, class V> RBTree<K, V>::RBTree(const RBTree & rb) :_size(rb._size) { Node* parent = NULL; _root = _Copy(rb._root, parent); } // 赋值重载 template<class K, class V> RBTree<K, V>& RBTree<K, V>::operator=(const RBTree &rb) { if (*this != &rb) { _Clear(_root); RBTree temp(rb); _size = temp._size; std::swap(_root, temp._root); } return *this; } // 插入元素 template<class K, class V> bool RBTree<K, V>::Insert(const K &key, const V &val) { // 1. 树为空的时候,直接插入 if (NULL == _root) { _root = new Node(key, val, BLACK); return true; } // 2. 树不为空 Node* pCur = _root; // 保存插入的结点 Node* pParent = NULL; // 保存插入结点的父亲,方便链接 // 1)、 开始找结点位置 while (pCur != NULL) { if (pCur->_key < key) { pParent = pCur; pCur = pCur->_right; } else if (pCur->_key > key) { pParent = pCur; pCur = pCur->_left; } else return false; } // 2)、位置已找到,插入并连接 pCur = new Node(key, val); if (pParent->_key < key) { pParent->_right = pCur; } else if (pParent->_key > key) { pParent->_left = pCur; } pCur->_parent = pParent; // 3. 判断是否需要调整树,可整合为如下情况 /************************************************************************/ /* 在分析之前要牢记:1.cur为当前结点 2.p为父节点 3.g为祖父结点 4.u为叔叔结点 /* 1. 当pCur为根节点时无需旋转调整,直接让根节点的颜色为黑即可,无需进入循环 /* /* 2. 当pCur不为根节点时,调整出现两个红色结点相连的情况,注意此时cur一定是红,p也是红,祖父为黑 /* a). 叔叔结点存在且为红色, 此时将父亲和叔叔结点调整为黑色,祖父变为红色,然后继续循环 /* b). 叔叔结点不存在或者为黑色,即 a)的 else情况 /* <1>. p是g的左孩子 /* 如果cur是p的左孩子 , 需要右旋g /* 如果cur是p的右孩子 , 先左旋p, 然后和上述<1>.中第一条情况一样 /* <2>. p是g的右孩子 /* 如果cur是p右孩子 , 需要左旋g /* 如果cur是p的左孩子 , 先右旋p, 然后和上述<2>. 中第一条情况一样 /* 完成后将 g 的颜色改为红色 p的颜色改为黑色 /************************************************************************/ while (pCur != _root && pParent->_color == RED) // 此条件相当于限制了树的高度大于2 { Node* pGrandfather = pParent->_parent; // 保存祖父结点 Node* pUnclue = NULL; // 保存叔叔结点 if (pParent == pGrandfather->_left) // 找到叔叔结点 pUnclue = pGrandfather->_right; else pUnclue = pGrandfather->_left; if (pUnclue != NULL && RED == pUnclue->_color) // 情况2.a { pGrandfather->_color = RED; pParent->_color = BLACK; pUnclue->_color = BLACK; } else { if (pParent == pGrandfather->_left) // 左子树 { if (pCur == pParent->_right) // 左右 { RBTree<K, V>::RotateLeft(pParent); std::swap(pCur, pParent); } RBTree<K, V>::RotateRight(pGrandfather); // 左左 } else // 右子树 { if (pCur == pParent->_left) // 右左 { RBTree<K, V>::RotateRight(pParent); std::swap(pCur, pParent); } RBTree<K, V>::RotateLeft(pGrandfather); // 右右 } pGrandfather->_color = RED; pParent->_color = BLACK; } pCur = pGrandfather; // 跳过黑色结点直接开始调整红色结点 pParent = pCur->_parent; } _root->_color = BLACK; _size++; return true; } // 判断是否满足红黑树性质 template<class K, class V> bool RBTree<K, V>::IsRBTree() { if (_root == NULL) return true; if (RED == _root->_color) // 红黑树性质以 return false; size_t leftBlackCount = 0; // 保存最左路径上黑结点个数 size_t rightBlackCount = 0; // 保存最右路径上黑结点个数 Node* lNext = _root; Node* rNext = _root; while (lNext) { if (lNext->_color == BLACK) leftBlackCount++; lNext = lNext->_left; } while (rNext) { if (rNext->_color == BLACK) rightBlackCount++; rNext = rNext->_right; } // 黑结点个数不同 if (leftBlackCount != rightBlackCount) return false; size_t curBlackCount = 0; // 保存每走一步黑结点个数 return RBTree<K, V>::_IsRBTree(_root, curBlackCount, curBlackCount); } // 中序打印 template<class K, class V> void RBTree<K, V>::Inorder() { cout << "Inorder : "; RBTree::_Inorder(_root); cout << endl; } // 查找key是否存在 template<class K, class V> bool RBTree<K, V>::Find(const K& key) { return _Find(_root, key); } // 结点个数 template<class K, class V> size_t RBTree<K, V>::Size() { return _size; } // 判空 template<class K, class V> bool RBTree<K, V>::IsEmpty() { return NULL == _root; } // 析构函数 template<class K, class V> RBTree<K, V>::~RBTree() { _Clear(_root); } ////////////////////////////////////////////////////////////////////////// /* 以下为类private 成员函数*/ ////////////////////////////////////////////////////////////////////////// // 切记返回值一定要给模板参数。。。。。 // 拷贝 template<class K, class V> RBTreeNode<K,V>* RBTree<K, V>::_Copy(const Node* rbNode, Node* parent) { Node* ptemp = NULL; if (rbNode != NULL) { ptemp = new Node(rbNode->_key, rbNode->_val, rbNode->_color); ptemp->_parent = parent; parent = ptemp; ptemp->_left = _Copy(rbNode->_left, parent); ptemp->_right = _Copy(rbNode->_right, parent); } return ptemp; } // 递归中序遍历 template<class K, class V> void RBTree<K, V>::_Inorder(Node *pNode) { if (pNode != NULL) { RBTree<K, V>::_Inorder(pNode->_left); cout << pNode->_val << " "; RBTree<K, V>::_Inorder(pNode->_right); } } // 左旋 template<class K, class V> void RBTree<K, V>::RotateLeft(Node* parent) { Node* subR = parent->_right; // 右孩子 Node* pGrangfather = parent->_parent; // 父亲 Node* subRL = subR->_left; // 右孩子的左孩子 if (subRL != NULL) { subRL->_parent = parent; // 1.如果subRL存在则改变其父指针域指向parent } parent->_right = subRL; // 2.改变parent的右指针域 parent->_parent = subR; // 3.改变parent的父指针域 subR->_left = parent; // 4. 改变subL的左指针域 subR->_parent = pGrangfather; // 5. 改变subR的父指针域 if (pGrangfather != NULL) { if (pGrangfather->_left == parent) pGrangfather->_left = subR; else pGrangfather->_right = subR; } else { _root = subR; } } // 右旋 template<class K, class V> void RBTree<K, V>::RotateRight(Node* parent) { Node* subL = parent->_left; // 左孩子 Node* pGrandfather = parent->_parent; // 父亲 Node* subLR = subL->_right; // 左孩子的右孩子 if (subLR != NULL) { subLR->_parent = parent; // 1. 如果subLR存在改变subLR的父指针域指向parent } parent->_left = subLR; // 2. 改变parent的左指针域 parent->_parent = subL; // 3. 改变parent的父指针域 subL->_right = parent; // 4. 改变subL的右指针域 subL->_parent = pGrandfather; // 5. 改变subL的父指针域 if (pGrandfather != NULL) // 6. 如果parnet存在父节点,则连接否则改变根节点 { if (pGrandfather->_left == parent) pGrandfather->_left = subL; else pGrandfather->_right = subL; } else { _root = subL; } } template<class K, class V> bool RBTree<K, V>::_Find(Node* pNode, const K& key) { if (pNode != NULL) { if (pNode->_key == key) return true; else if (pNode->_key < key) return _Find(pNode->_left,key); else return _Find(pNode->_right, key); } return false; } // 判断是不是红黑树 template<class K, class V> bool RBTree<K, V>::_IsRBTree(Node* pNode, const size_t blackCount, size_t curBlackCount) { if (NULL == pNode) return true; if (pNode->_color == RED && pNode->_parent != NULL && pNode->_parent->_color == RED) return false; if (pNode->_color == BLACK) curBlackCount++; if (pNode->_left == NULL && pNode->_right == NULL) if (blackCount == curBlackCount) return false; return RBTree<K, V>::_IsRBTree(pNode->_left, blackCount, curBlackCount) && RBTree<K, V>::_IsRBTree(pNode->_right, blackCount, curBlackCount); } // 释放所有节点 template<class K, class V> void RBTree<K, V>::_Clear(Node*& pNode) { if (pNode != NULL) { _Clear(pNode->_left); _Clear(pNode->_right); delete pNode; pNode = NULL; } }
test.cpp
#include "RBTree.cpp" #include "RBTree.hpp" #define ARRAYSIZE(arr) sizeof(arr)/sizeof(arr[0]) void TestRBTreeInsert() { // test1 int array1[] = { 10, 8, 7, 15, 5, 6, 11, 13, 12 }; RBTree<int, int> rb1; for (size_t idx = 0; idx <ARRAYSIZE(array1); ++idx) { rb1.Insert(array1[idx], array1[idx]); rb1.Inorder(); cout << "插入 " << array1[idx] << " 满足红黑树性质否? " << boolalpha << rb1.IsRBTree() << endl; } cout << "-------------------------------------------------------" << endl; // test2 int array2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; RBTree<int, int> rb2; for (size_t idx = 0; idx < ARRAYSIZE(array2); ++idx) { rb2.Insert(array2[idx], array2[idx]); rb2.Inorder(); cout << "插入 " << array2[idx] << " 满足红黑树性质否? " << boolalpha << rb2.IsRBTree() << endl; } cout << "-------------------------------------------------------" << endl; // test3 RBTree<int, int> rb3; int array3[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 1, 3, 2, 1, 0 }; for (size_t idx = 0; idx < ARRAYSIZE(array3); ++idx) { rb3.Insert(array3[idx], array3[idx]); rb3.Inorder(); cout << "插入 " << array3[idx] << " 满足红黑树性质否? " << boolalpha << rb3.IsRBTree() << endl; } } void TestCopyCon_Assign() { // 测试拷贝构造和复制运算符重载 RBTree<string, int> rb; rb.Insert("张三", 22); rb.Insert("里斯", 18); rb.Insert("呵呵", 12); rb.Inorder(); RBTree<string, int> rbcopy(rb); rbcopy.Inorder(); } void TestOtherFun() { RBTree<string, int> rb; cout << rb.Size() << endl; cout << boolalpha << rb.IsEmpty() << endl; rb.Insert("张三", 22); rb.Insert("里斯", 18); cout << boolalpha << rb.Find("张三") << endl; cout << boolalpha << rb.Find("不存在") << endl; rb.Insert("呵呵", 12); cout << rb.Size() << endl; rb.Inorder(); cout << boolalpha << rb.IsEmpty() << endl; rb.~RBTree(); } int main() { //TestRBTreeInsert(); //TestCopyCon_Assign(); TestOtherFun(); return 0; }
相关文章推荐
- 数据结构::模拟STL实现list
- (二)数据结构学习笔记 简单堆栈,STL中堆栈的实现
- 红黑树实现——STL中的map
- c++ STL 红黑树实现
- STL实现的底层数据结构简介
- STL中map、set的数据结构及底层实现
- STL中map、set的数据结构及底层实现
- Chapter 3 | Stacks and Queues--实现数据结构模拟一个栈由几个子栈组成,并可以在指定子栈上进行出栈操作
- 重温数据结构系列随笔:单链表(c#模拟实现)
- STL中map、set的数据结构及底层实现
- STL中map、set的数据结构及底层实现
- STL实现的底层数据结构简介
- STL实现的底层数据结构简介
- 红黑树实现——STL中的MAP
- STL实现的底层数据结构(转载)
- STL中map、set的数据结构及底层实现 (转载)
- 重温数据结构系列随笔:单链表(c#模拟实现)
- STL中map、set的数据结构及底层实现
- STL中map、set的数据结构及底层实现
- STL中map、set的数据结构及底层实现