map的简单模拟实现
来源:互联网 发布:vc60怎么编写c语言 编辑:程序博客网 时间:2024/05/29 03:42
在上一篇文章中提到了STL中map和set的基本用法,也了解到map和set的底层实现是一颗红黑树,也就是说map的增删查改也就是红黑树的增删查改,再加一层迭代器,map就可以使用红黑树中的基本操作了。
map&set学习总结
什仫是红黑树?
一棵二叉树要是红黑树首先需要是一颗二叉搜索树,不同于二叉搜索树的是它增加了一个存储位来表示结点的颜色,这个颜色可以是RED也可以是BLACK,通过对任何一条从根到叶子简单路径上的颜色来约束。红黑树保证最长路径不超过最短路径的2倍,因而是近似于平衡的二叉树。
红黑树的性质?
1.每个结点不是红色就是黑色的;
2.根结点是黑色的;
3.如果一个结点是红色的,那仫它的父亲和孩子一定是黑色的(不存在连续的红节点);
4.每条路径上黑色结点的数量是相等的;
5.每个叶子结点都是黑色的(这里的叶子结点指的是空结点);
红黑树的插入:
为了满足上述红黑树的特性,我们将新插入的结点的颜色设置为红色。红黑树的插入有以下几种情况:
1.如果要插入的结点是根结点,则直接插入并修改根结点的颜色为黑色;
2.如果要插入结点的位置的父结点是黑色的,则直接插入;
3.如果要插入结点的位置的父结点是红色的,此时再次插入一个红节点,就会出现连续的红结点,此时就不满足红黑树的性质了就需要分情况处理了。设要插入的位置是cur,它的父亲是parent,它的祖父是grandfather,它的叔叔结点是uncle。
如果父结点是红色的,那仫一定存在祖父结点且是祖父结点是黑色的。
1>.uncle存在且uncle是红色的
2>.uncle不存在,或者uncle是黑色的,且cur是parent的左孩子
3>.uncle不存在,或者uncle是黑色的,且cur是parent的右孩子
红黑树的删除:
红黑树的删除的操作类似二叉搜索树的删除,但是红黑树在删除之后还要恢复成一颗满足红黑树性质的树。主要做法是:先找到该结点,如果该结点的左右都不为空,我们可以考虑用替换法来删除该结点,也就是说找一个可以代替该删除结点的结点,删除的是这个替换的结点。我们可以在该结点右子树中找最左结点也可以在左子树中找最右结点(由搜索二叉树的性质来决定)。此时删除就会归类为左为空或者右为空的情况,下面讨论一下红黑树的删除操作。
1.删除的结点是红色的,则直接删除,此时是不会影响红黑树的平衡的;
2.删除的结点是黑色的,但是该结点的孩子是红色的,则删除该结点之后要将它的孩子结点变成黑色的;
在情况2的条件下,如果要删除结点的父结点是根结点需要特殊处理;
3.要删除的结点是黑色的,它的孩子也是黑色的。此时根据它的兄弟结点的颜色又分为几种情况。假设要删除的结点cur是它的父亲结点parent的右孩子,它的兄弟结点是brother;
1>.brother结点是红色的,根据红黑树的性质,可知,父结点parent一定存在且是黑色的,brother的孩子结点bL,bR也一定是黑色的。此时的处理操作是以parent为轴进行右单旋,再将parent和brother变色,parent变为红色,brother变为黑色;
上面那种情况进行右单旋并且修改parent和brother的颜色,此时brother左子树的黑色结点数量并没有发生变化,但是右子树的黑色结点的数量总是少一个,此时应该调整该树,使其以cur为起点继续平衡;
2>.brother是黑色的,此时根据bL和bR的颜色又会继续划分情况
情况一:bL和bR都是黑色的
1>>.parent是红色的
此时parent是红色的,brother和它的孩子bL和bR都是黑色的,此时只需要将parent和brother的颜色对调即可,parent变为黑色,brother变为红色。这就相当于在cur这条路径上增加了一个黑色结点,在brother这条路径上减少一个黑色结点又增加一个黑色结点,这棵树已经处于平衡的状态了。
2>>.parent是黑色的
此时parent是黑色的,brother和它的孩子bL和bR也都是黑色的,此时要做的处理操作是以parent为轴进行右单旋,旋转完成之后将parent的颜色变为红色,此时brother左右子树的黑色结点的数量是相等的,但是比之前的都减少了1,所以此时需要以brother为起点继续开始调整;
情况二:bL是红色的,bR的颜色任意
此时的brother是黑色,bL是红色,parent和bR的颜色任意,此时的处理操作是以parent为轴进行的右单旋,再将bL变为黑色,此时brother结点额左右子树的黑色结点的数量是相等的,这棵树满足红黑树的特性;
情况三:bL是黑色的,bR是红色的
此时的brother,bL是黑色的,bR是红色的,parent的颜色任意。这个时候的处理操作是以brother为轴进行的左单旋,旋转之后修改brother和bR的颜色,brother变为红色,bR变为黑色。这个时候就和情况相同的了,转为情况二来处理。
上面介绍的都是cur为右孩子的情况,如果cur为左孩子,则处理方式是类似的,只需要修改镜像指针即可。
例子:模拟红黑树的实现,写出函数判断一棵树是否是红黑树,并利用红黑树模拟map的实现,包括对map操作的迭代器的实现。
enum Color{ RED, BLACK,};template<class KV>struct RBTreeNode{ KV _kv; RBTreeNode<KV> *_left; RBTreeNode<KV> *_right; RBTreeNode<KV> *_parent; Color color; RBTreeNode(const KV& kv) //新增结点是红色的 :_kv(kv) ,_left(NULL) ,_right(NULL) ,_parent(NULL) ,color(RED) {}};//红黑树的迭代器实现template<class KV,class KVRef,class KVPtr>struct RBTreeIterator{ typedef RBTreeNode<KV> Node; typedef RBTreeIterator<KV,KVRef,KVPtr> Self; RBTreeIterator(Node *node) :_node(node) {} KVRef operator*() { return _node->_kv; } KVPtr operator->() { return &(operator*()); } Self& operator++() //找中序遍历的下一个结点 { if(_node->_right == NULL) //右树为空 { Node *cur=_node; Node *parent=cur->_parent; while(parent) { if(parent->_left == cur) //cur是parent的左 { _node=parent; break; } else //cur是parent的右 { cur=parent; parent=cur->_parent; } } // _node=parent; } else //右树不为空 { Node *subLeft=_node->_right; //找右树的最左结点 while(subLeft->_left) { subLeft=subLeft->_left; } _node=subLeft; } return *this; } bool operator!=(const Self& s)const { return _node != s._node; } Node *_node;};template<class K,class V>class RBTree{ typedef RBTreeNode<pair<K,V>> Node;public: typedef RBTreeIterator<pair<K,V>,pair<K,V>&,pair<K,V>*> Iterator; RBTree() :_root(NULL) {} ~RBTree() { _destroy(_root); } Iterator Begin() //返回最左结点 { Node *cur=_root; while(cur && cur->_left) { cur=cur->_left; } return Iterator(cur); } Iterator End() { return Iterator(NULL); } bool Insert(const pair<K,V>& kv) { if(_root == NULL) //根结点是黑色的 { _root=new Node(kv); _root->color=BLACK; return true; } Node *parent=NULL; Node *cur=_root; while(cur) { if(cur->_kv.first < kv.first) { parent=cur; cur=cur->_right; } else if(cur->_kv.first > kv.first) { parent=cur; cur=cur->_left; } else //找到了 { return false; } } cur=new Node(kv); //插入新节点 if(parent->_kv.first < kv.first) { parent->_right=cur; cur->_parent=parent; } else { parent->_left=cur; cur->_parent=parent; } while(parent && parent->color == RED) //parent存在且parent的颜色是红色的,不存在连续的红结点 { Node *grandfather=parent->_parent; //祖父结点 if(grandfather->_left == parent) { Node *uncle=grandfather->_right; if(uncle && uncle->color == RED) //uncle存在且uncle是红色结点 { parent->color=uncle->color=BLACK; grandfather->color=RED; cur=grandfather; parent=cur->_parent; } else //uncle不存在或者是uncle是黑色结点 { if(parent->_right == cur) //情况3 { RotateL(parent); swap(cur,parent); //旋转之后,交换cur和parent的位置 } RotateR(grandfather); //情况2 parent->color=BLACK; grandfather->color=RED; break; } } else { Node *uncle=grandfather->_left; if(uncle && uncle->color == RED) { parent->color=uncle->color=BLACK; grandfather->color=RED; cur=grandfather; parent=cur->_parent; } else //uncle不存在或者为黑色 { if(parent->_left == cur) { RotateR(parent); swap(cur,parent); } RotateL(grandfather); parent->color=BLACK; grandfather->color=RED; break; } } } _root->color=BLACK; //根结点是黑色的 return true; } bool Find(const K& key) { Node *cur=_root; while(cur) { if(cur->_kv.first < key) { cur=cur->_right; } else if(cur->_kv.first > key) { cur=cur->_left; } else //找到了 { return true; } } return false; } Node *FindMinNode() //最小结点是最左结点 { Node *cur=_root; while(cur->_left) { cur=cur->_left; } return cur; } Node *FindMaxNode() //最大结点是最右结点 { Node *cur=_root; while(cur->_right) { cur=cur->_right; } return cur; } bool IsRBTree() { if(_root == NULL) return true; if(_root->color == RED) //根结点是黑色结点 return false; Node *cur=_root; int blackNum=0; //统计其中一条路径上黑色结点的数量,基准值 while(cur) { if(cur->color == BLACK) { blackNum++; } cur=cur->_left; } int count=0; //计数器,通过计算其他路径上黑色结点的数量,与基准值做对比 return _IsRBTree(_root,blackNum,count); } void InOrder() { _InOrder(_root); cout<<endl; }private: bool _IsRBTree(Node *root,const int blackNum,int count) { if(root == NULL) return true; if(root->color == RED && root->_parent->color == RED) { cout<<"存在连续的红结点"<<endl; return false; } if(root->color == BLACK) ++count; if(root->_left == NULL && root->_right == NULL) { if(count != blackNum) { cout<<"黑色结点的数量不相等"<<endl; return false; } } return _IsRBTree(root->_left,blackNum,count) \ && _IsRBTree(root->_right,blackNum,count); } void _InOrder(Node *root) { if(root == NULL) return; _InOrder(root->_left); cout<<root->_key<<" "; _InOrder(root->_right); } void 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 == NULL { _root=subR; subR->_parent=NULL; } else { if(ppNode->_left == parent) ppNode->_left=subR; else ppNode->_right=subR; subR->_parent=ppNode; } } void 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; parent->_parent=subL; if(ppNode == NULL) //_root == NULL { _root=subL; subL->_parent=NULL; } else { if(ppNode->_left == parent) ppNode->_left=subL; else ppNode->_right=subL; subL->_parent=ppNode; } } void _destroy(Node *&root) { if(root == NULL) return; _destroy(root->_left); _destroy(root->_right); delete root; }private: Node *_root;};void TestMap(){ RBTree<string,string> dict; dict.Insert(make_pair<string,string>("left","左边")); dict.Insert(make_pair<string,string>("right","右边")); dict.Insert(make_pair<string,string>("sort","排序")); RBTree<string,string>::Iterator it=dict.Begin(); while(it != dict.End()) { cout<<it->first<<" "<<it->second<<endl; ++it; }}
在这里就分享结束了~~~
- map的简单模拟实现
- JavaScript模拟Map的实现
- python:模拟内置函数map的实现
- JS模拟的Map类实现方法
- map/reduce的简单实现
- JavaScript实现简单的Map
- BIT-MAP的简单实现
- javascript实现简单的Map
- JS map的简单实现
- RSA的简单模拟实现
- 模拟计算器的简单实现
- 简单模拟vector的实现
- javascript模拟实现Map
- ural 2002. Test Task 简单模拟 stl map的使用
- 模拟share_ptr的实现-01-简单实现
- 使用Javascript实现简单的Map
- 使用Javascript实现简单的Map
- 自己实现的简单List和Map
- leetcode第十八周解题总结-贪心算法
- 概念
- 性能测试工具JMeter使用指南
- 更详细的天气预报接口
- ActiveMQ消息传送机制以及ACK机制详解
- map的简单模拟实现
- websphere8.5配置db2数据源
- LINQ系列:LINQ to ADO.NET概述
- HDU 4734 数位DP 解题报告
- 数据库索引设计与优化读书笔记--《三》SQL处理过程
- LINQ系列:LINQ to DataSet的DataTable操作
- protobuf入门教程
- ROS教程:视觉传感器使用漫谈
- git cherry-pick