模拟实现map&set
文章目录
1. map的特性
1. map是关联容器,它按照特定的次序
(按照key来比较)存储由键值key和值value组合而成的元素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
typedef pair value_type
;
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))
2. map的模板参数说明
- key: 键值对中key的类型
- T: 键值对中value的类型
- Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
- Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
注意:在使用map时,需要包含头文件
3. map的构造
函数声明 | 功能介绍 |
---|---|
map() | 构造一个空的map |
4. map的迭代器
5. map的容量与元素访问
函数声明 | 功能简介 |
---|---|
bool empty ( ) const | 检测map中的元素是否为空,是返回true,否则返回false |
size_type size() const | 返回map中有效元素的个数 |
mapped_type& operator[] (constkey_type& k) | 返回去key对应的value |
注意当key不存在时,mapped_type& operator[] (constkey_type& k)相当于用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常
6. map中元素的修改
函数声明 | 功能简介 |
---|---|
pair<iterator,bool> insert (const value_type& x ) | 在map中插入键值对x,注意x是一个键值对,返回值也是键值对:iterator代表新插入元素的位置,bool代表释放插入成功 |
void erase ( iterator position ) | 删除position位置上的元素 |
size_type erase ( const key_type& x) | 删除键值为x的元素 |
void erase ( iterator first,iterator last ) | 删除[first, last)区间中的元素 |
void swap (map<Key,T,Compare,Allocator>&mp) | 交换两个map中的元素 |
void clear ( ) | 将map中的元素清空 |
iterator find ( const key_type& x) | 在map中插入key为x的元素,找到返回该元素的位置的迭代器,否则返回end |
const_iterator find ( const key_type& x ) const | 在map中插入key为x的元素,找到返回该元素的位置的const迭代器,否则返回cend |
size_type count ( const key_type& x ) const | 返回key为x的键值在map中的个数,注意map中key是唯一的,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中 |
【总结】
- map中的的元素是键值对
- map中的key是唯一的,并且不能修改
- 默认按照小于的方式对key进行比较
- map中的元素如果用迭代器去遍历,可以得到一个有序的序列
- map的底层为平衡搜索树(红黑树),查找效率比较高
- 支持[]操作符,operator[]中实际进行插入查找。
set/multimap/multiset的接口和map基本类似,主要的区别在于元素能否重复,是否可以使用operator[]操作
7. map的模拟实现
map的底层使用的是红黑树,所以先封装一个红黑树
红黑树
要实现map还需要在前文的基础上进行填充一些操作:
1. 红黑树的迭代器:
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
begin()与end()
- STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一 个有序的序列,
- 因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?
- 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置
2. operator++()与operator–()
// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{
//分两种情况讨论:_pNode的右子树存在和不存在
// 右子树存在
if(_pNode->_pRight)
{
// 右子树中最小的节点,即右子树中最左侧节点
_pNode = _pNode->_pRight;
while(_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{
// 右子树不存在,向上查找,直到_pNode != pParent->right
PNode pParent = _pNode->_pParent;
while(pParent->_pRight == _pNode)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
// 特殊情况:根节点没有右子树
if(_pNode->_pRight != pParent)
_pNode = pParent;
}
}
// 获取迭代器指向节点的前一个节点
void Decreasement()
{
//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不存在
// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置
if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
else if(_pNode->_pLeft)
{
// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点
_pNode = _pNode->_pLeft;
while(_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else
{
// _pNode的左子树不存在,只能向上找
PNode pParent = _pNode->_pParent;
while(_pNode == pParent->_pLeft)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
3. 2 改造红黑树
// 因为关联式容器中存储的是<key, value>的键值对,因此
// k为key的类型,
// ValueType: 如果是map,则为pair<K, V>; 如果是set,则为k
// KeyOfValue: 通过value来获取key的一个仿函数类
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
typedef RBTreeNode<ValueType> Node;
typedef Node* PNode;
public:
typedef RBTreeIterator<ValueType, ValueType*, ValueType&> Iterator;
public:
RBTree();
~RBTree()
/////////////////////////////////////////////////////////////////////
// Iterator
Iterator Begin(){ return Iterator(_pHead->_pLeft);}
Iterator End(){ return Iterator(_pHead);}
//////////////////////////////////////////////////////////////////
// Modify
pair<Iterator, bool> Insert(const ValueType& data)
{
// 插入节点并进行调整
// 参考上文...
return make_pair(Iterator(pNewNode), true);
}
// 将红黑树中的节点清空
void Clear();
Iterator Find(const K& key);
//////////////////////////////////////////////////////////////////
// capacity
size_t Size()const;
bool Empty()const;
// ……
private:
PNode _pHead;
size_t _size; // 红黑树中有效节点的个数
};
RBTree.hpp
#pragma once
#include<algorithm>
enum Color {
RED,
BLACK
};
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(), Color color = RED)
:_pLeft(nullptr)
,_pRight(nullptr)
,_pParent(nullptr)
,_data(data)
,_color(color)
{}
RBTreeNode<ValueType>* _pLeft;
RBTreeNode<ValueType>* _pRight;
RBTreeNode<ValueType>* _pParent;
ValueType _data;
Color _color;
};
template<class T>
class Iterator {
public:
typedef RBTreeNode<T> Node;
typedef Iterator<T> Self;
Iterator(Node* pNode=nullptr)
:_pNode(pNode)
{}
T& operator*() {
return _pNode->_data;
}
T* operator->() {
return &(_pNode->_data);
}
Self& operator++() {
Next();
return *this;
}
Self operator++(int) {
Self tmp(_pNode);
Next();
return tmp;
}
bool operator==(Self& t){
return _pNode == t._pNode;
}
bool operator!=(Self& t){
return _pNode != t._pNode;
}
Self& operator--() {
Prev();
return *this;
}
Self operator--(int) {
Self tmp(_pNode);
Prev();
return tmp;
}
private:
void Next() {
//如果有右子树
if (_pNode->_pRight) {
_pNode = _pNode->_pRight;
while (_pNode->_pLeft)
_pNode = _pNode->_pLeft;
return;
}
Node* pParent = _pNode->_pParent;
while (pParent->_pRight == _pNode) {
_pNode = pParent;
pParent = _pNode->_pParent;
}
//根节点没有右子树,并且迭代器刚好在根节点位置
if (_pNode->_pRight != pParent)
_pNode = pParent;
}
void Prev() {
//1._pNode在head位置(即end()位置),应该将_pNode放在最大结点处
if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
//2. 如果有左子树
else if (_pNode->_pLeft) {
_pNode = _pNode->_pLeft;
while (_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else {
Node* pParent = _pNode->_pParent;
while (pParent->_pLeft == _pNode) {
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
Node* _pNode;
};
template<class T,class KorV>
class RBTree {
public:
typedef RBTreeNode<T> Node;
typedef Node* PNode;
typedef RBTree<T, KorV> Self;
typedef Iterator<T> iterator;
public:
RBTree()
:_pHead(new Node)
,_size(0)
{
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
}
~RBTree() {
if (_pHead->_pParent)
del(_pHead->_pParent);
delete _pHead;
}
std::pair<iterator,bool> Insert(const T& data) {
PNode& pRoot = GetRoot(); //获取根节点
PNode newPtr = nullptr;
if (nullptr == pRoot) { //如果红黑树为空
newPtr = pRoot = new Node(data, BLACK);
pRoot->_pParent = _pHead;
}
else {
PNode pParent = nullptr;
PNode pCur = pRoot;
//插入节点
while (pCur) {
pParent = pCur;
if (KorV()(data) < KorV()(pCur->_data))
pCur = pCur->_pLeft;
else if (KorV()(data) > KorV()(pCur->_data))
pCur = pCur->_pRight;
else
return std::make_pair(iterator(), false);
}
newPtr = pCur = new Node(data);
if (KorV()(data) < KorV()(pParent->_data)) {
pParent->_pLeft = pCur;
pCur->_pParent = pParent;
}
else {
pParent->_pRight = pCur;
pCur->_pParent = pParent;
}
//检测新节点插入后。红黑树的性质是否遭到破坏
while (pParent != _pHead && RED == pParent->_color) {
PNode pGrand = pParent->_pParent;
//pParent在pGrand左侧的情况
if (pParent == pGrand->_pLeft) {
PNode unclue = pGrand->_pRight;
//情况一
if (unclue&&RED == unclue->_color) {
pParent->_color = BLACK;
unclue->_color = BLACK;
pGrand->_color = RED;
pCur = pGrand;
pParent = pCur->_pParent;
}
else {
//情况三
if (pParent->_pRight == pCur) {
RotateLeft(pParent);
std::swap(pParent, pCur);
}
//情况二
RotateRight(pGrand);
pParent->_color = BLACK;
pGrand->_color = RED;
}
}
//pParent在pGrand右侧的情况
else {
PNode unclue = pGrand->_pLeft;
//情况一
if (unclue&&RED == unclue->_color) {
pParent->_color = BLACK;
unclue->_color = BLACK;
pGrand->_color = RED;
pCur = pGrand;
pParent = pCur->_pParent;
}
else {
//情况三
if (pParent->_pLeft == pCur) {
RotateRight(pParent);
std::swap(pParent, pCur);
}
//情况二
RotateLeft(pGrand);
pParent->_color = BLACK;
pGrand->_color = RED;
}
}
}
}
//根节点的颜色可能被修改,将其改回黑色
pRoot->_color = BLACK;
//更新头结点的左右孩子
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
++_size;
return std::make_pair(iterator(newPtr), true);
}
void Inorder()
{
_InOrder(GetRoot());
std::cout << std::endl;
}
bool IsValidRBTree()
{
PNode pRoot = GetRoot();
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color)
{
std::cout << "违反红黑树性质二:根节点必须为黑色" << std::endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
iterator find(const T& data)const {
PNode ptr = GetRoot();
while (ptr) {
if (KorV()(data) == KorV()(ptr->_data))
return iterator(ptr);
else if (KorV()(data) < KorV()(ptr->_data))
ptr = ptr->_pLeft;
else
ptr = ptr->_pRight;
}
return end();
}
size_t size()const {
return _size;
}
bool empty()const {
return _size == 0;
}
iterator begin() {
return iterator(_pHead->_pLeft);
}
iterator end() {
return iterator(_pHead);
}
private:
void _InOrder(Node* pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
std::cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
void del(PNode ptr) {
if (ptr->_pLeft)
del(ptr->_pLeft);
if (ptr->_pRight)
del(ptr->_pRight);
delete ptr;
}
bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount) {
//走到null之后,判断k和black是否相等
if (nullptr == pRoot)
{
if (k != blackCount)
{
std::cout << "违反性质四:每条路径中黑色节点的个数必须相同" << std::endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
std::cout << "违反性质三:没有连在一起的红色节点" << std::endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
PNode& GetRoot() {
return _pHead->_pParent;
}
PNode LeftMost() {
PNode ptr = GetRoot();
if (!ptr)
return _pHead;
while (ptr->_pLeft)
ptr = ptr->_pLeft;
return ptr;
}
PNode RightMost() {
PNode ptr = GetRoot();
if (!ptr)
return _pHead;
while (ptr->_pRight)
ptr = ptr->_pRight;
return ptr;
}
void RotateLeft(PNode pParent) {
PNode pPParent = pParent->_pParent;
PNode pRight = pParent->_pRight;
pParent->_pRight = pRight->_pLeft;
if (pRight->_pLeft)
pRight->_pLeft->_pParent = pParent;
pRight->_pLeft = pParent;
pParent->_pParent = pRight;
//当pPParent不存在时
if (pPParent==_pHead) {
//设置pRight为根节点
_pHead->_pParent = pRight;
pRight->_pParent = _pHead;
}
else {
if (pPParent->_pLeft == pParent) {
pPParent->_pLeft = pRight;
pRight->_pParent = pPParent;
}
else {
pPParent->_pRight = pRight;
pRight->_pParent = pPParent;
}
}
}
void RotateRight(PNode pParent) {
PNode pPParent = pParent->_pParent;
PNode pLeft = pParent->_pLeft;
pParent->_pLeft = pLeft->_pRight;
if (pLeft->_pRight)
pLeft->_pRight->_pParent = pParent;
pLeft->_pRight = pParent;
pParent->_pParent = pLeft;
//当pPParent不存在时
if (pPParent == _pHead) {
//设置pLeft为根节点
_pHead->_pParent = pLeft;
pLeft->_pParent = _pHead;
}
else {
if (pPParent->_pLeft == pParent) {
pPParent->_pLeft = pLeft;
pLeft->_pParent = pPParent;
}
else {
pPParent->_pRight = pLeft;
pLeft->_pParent = pPParent;
}
}
}
private:
PNode _pHead; //头结点(根节点的父亲节点)
size_t _size;
};
map.hpp
#include"RBTree.hpp"
template<class K,class V>
class Map {
typedef std::pair<K, V> ValueType;
struct KorV {
const K& operator()(const ValueType& v)const{
return v.first;
}
};
typename typedef RBTree<ValueType, KorV>::iterator iterator;
public:
Map()
{}
iterator begin() {
return _tree.begin();
}
iterator end() {
return _tree.end();
}
size_t size()const {
return _tree.size();
}
bool empty()const {
return _tree.empty();
}
std::pair<iterator, bool> insert(const ValueType& data) {
return _tree.Insert(data);
}
iterator find(const K& key)const{
return _tree.find(ValueType(key, V()));
}
V& operator[](const K& k) {
return (*(insert(ValueType(k, V())).first)).second;
}
private:
RBTree<ValueType, KorV> _tree;
};
8. set的模拟实现
set.hpp
#include"RBTree.hpp"
template<class K>
class Set {
typedef K ValueType;
struct KorV {
const K& operator()(const ValueType& data) {
return data;
}
};
public:
typename typedef RBTree<ValueType, KorV>::iterator iterator;
public:
Set()
{}
iterator begin() {
return _tree.begin();
}
iterator end() {
return _tree.end();
}
std::pair<iterator, bool> insert(const ValueType& data) {
return _tree.Insert(data);
}
size_t size()const {
return _tree.size();
}
bool empty()const {
return _tree.empty();
}
iterator find(const K& key) {
return _tree.find(key);
}
private:
RBTree<ValueType, KorV> _tree;
};