前言:
C++中的
map
是标准模板库(STL)中的一种关联容器,它存储的是键值对(key-value pairs),其中每个键都是唯一的。
map的特点
- 值唯一性:map中的每个键只能出现一次,如果尝试插入具有相同键的新元素,则会覆盖原有键对应的值。
- 自动排序:map中的元素是根据键值自动排序的,默认情况下是按照键的升序排序。
- 动态调整:插入和删除操作会自动调整内部结构以保持有序性和平衡性。
- 支持复杂数据类型:map的键和值可以是任意数据类型,包括自定义类型。
- 迭代器支持:map提供双向迭代器,可以遍历容器中的所有元素。
- 成员函数丰富:map提供了一系列成员函数,如insert、erase、find、count、begin、end等,用于执行各种操作。
- 空间和性能开销:由于使用红黑树,map可能会有较大的空间开销,并且相比于哈希表(如unordered_map),插入和删除操作的时间复杂度为对数级别,可能在某些高性能要求的应用中不如哈希表高效
map的实现
map的存储结构
- 由于set和map是公用一棵树,set是K 、map是KV的结构,为了适应map的结构,set做出一定的改变
- 内部类是为了取到map内所存储的数据
- 在set的map中 set所存储的是key而map是pair。这里取到的值是first。
template<class K, class V>
class map
{
struct mapofT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
private:
RBTree<K, pair<const K, V>, mapofT> _t;
};
map的插入
插入实现的基本步骤
查找插入位置**:map内部使用红黑树来维护元素的顺序,因此插入操作首先需要在树中找到正确的位置。这通常涉及到比较键值并沿着树进行遍历。
创建节点:一旦找到了插入位置,就会创建一个新的节点来存储要插入的键值对。
插入节点:新创建的节点会被插入到红黑树中的正确位置,这可能涉及到树的旋转和重新着色操作,以保持红黑树的平衡性质。
返回插入结果:insert函数返回一个pair,其中第一个元素是一个迭代器,指向新插入的元素(如果键不存在),或者指向已存在键的元素;第二个元素是一个布尔值,指示插入是否成功(即键是否不存在)。
pair<iterator,bool> insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root),true);
}
Node* parent = nullptr;
Node* cur = _root;
keyofT kot;
while (cur)
{
if (kot(data) > kot(cur->_data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(data) < kot(cur->_data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(data) < kot(parent->_data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
Node* uncle = grandparent->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
else//uncle is not or black
{
if (cur == parent->_left)
{
RotateR(grandparent);
grandparent->_col = RED;
parent->_col = BLACK;
}
else
{
RotateL(parent);
RotateR(grandparent);
grandparent->_col = RED;
cur->_col = BLACK;
}
break;
}
}
else//parent == grandparent->_right
{
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
}
break;
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode),true );
}
map的查找
- 调用
find
成员函数,传入要查找的键作为参数。 find
函数在内部遍历红黑树,使用键值进行比较,寻找匹配的元素。- 如果找到了匹配的键,
find
返回一个指向该元素的迭代器。 - 如果没有找到匹配的键,
find
返回end()
迭代器。 - 红黑树的查找类似于,AVL的查找。本质上是一样的。
Node* find(const T& data)
{
keyofT kot;
Node* cur = _root;
while (cur)
{
if (kot(data) > kot(cur->_data))
{
cur = cur->_right;
}
else if (kot(data) < kot(cur->_data))
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
map重载[]
- STL中也是调用insert。
- 传入K返回V的形式。
V& operator[] (const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key,V()));
return ret.first->second;
}
map的迭代器
C++中的map是一个关联容器,它存储键值对(key-value pairs),并且根据键(key)来快速检索值(value)。map的迭代器是一种指针,指向map中的元素。map的迭代器有两种类型:const_iterator和iterator。const_iterator用于只读访问,而iterator可以读写。
迭代器的特性
map
的迭代器遵循双向迭代器的要求,可以向前和向后遍历。- 迭代器在
map
被修改时(例如插入或删除操作)可能会失效。 map
的迭代器可以通过解引用操作符(*
)来访问指向的元素,或者使用箭头操作符(->
)来访问元素的成员。set迭代器类似于list的迭代器
迭代器的封装
- 主要区别在于
++
和--
- Ptr是T*,Ref是T&,设置这么多的模板参数是为适配出const的迭代器
template<class T,class Ptr,class Ref>
struct TreeIterator
{
typedef RBTreeNode<T> Node;
typedef TreeIterator<T,Ptr,Ref> self;
typedef TreeIterator<T, T*, T&> Iterator;
Node* _node;
TreeIterator(Node* node)
:_node(node)
{
}
TreeIterator(const Iterator& it)
:_node(it._node)
{
}
Ref operator* ()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator != (const self & s) const
{
return _node!= s._node;
}
bool operator== (const self & s) const
{
return _node == s._node;
}
};
前置++
- 前置++ 的本质上就是中序的遍历,左子树-根-右子树
- 如果右子树存在就去右子树的最左节点
- _node 不是右节点那么,证明子树的左右节点均访问完成,需要访问祖父的节点
self& operator++()
{
if (_node->_right)
{
Node* cur = _node->_right;
while (cur->_left)
{
cur = cur->_left;
}
_node = cur;
}
else
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent)
{
if (cur == parent->_left)
{
break;
}
else
{
cur = cur->_parent;
parent = parent->_parent;
}
}
_node = parent;
}
return *this;
}
前置--
- 前置-- 是前置++的镜像的一个顺序的访问,右子树-根-左子树
- 方法是类似于前置++
self& operator--()
{
if (_node->_left)
{
Node* cur = _node->_right;
while (cur->_left)
{
cur = cur->_right;
}
_node = cur;
}
else
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent && parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
迭代器
C++中的map是一种关联容器,它存储键值对(key-value pairs),并且按照键的顺序自动排序。map的迭代器用于遍历容器中的元素。map提供了多种迭代器成员函数,包括:
- begin():返回指向map中第一个元素的迭代器。
- end():返回指向map容器末尾位置的迭代器,这个位置不包含任何元素,用作遍历结束的标志。
- rbegin():返回指向map最后一个元素的反向迭代器。
- rend():返回指向map第一个元素的反向迭代器。
- map的迭代器是const_iterator类型的,这意味着它们提供对元素的只读访问。如果需要修改元素,可以使用iterator类型
map的迭代器提供了向前和向后遍历的能力,但不支持随机访问迭代器的所有操作,如算术运算
前面是将迭代器封装,因为正常的++或者--已不可以支持自定义类型的加减
- 在红黑树的类中实现,在实现set时候只需要调用,在这里面可以认为红黑树是容器的适配器
typedef TreeIterator<T, T*, T&> iterator;
typedef TreeIterator<T,const T*,const T&> const_iterator;
iterator begin()
{
Node* leftmin = _root;
while (leftmin->_left)
{
leftmin = leftmin->_left;
}
return iterator(leftmin);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin()const
{
Node* leftmin = _root;
while (leftmin->_left)
{
leftmin = leftmin->_left;
}
return const_iterator(leftmin);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
源码
map
#pragma once
#include"RBTree.h"
#include<iostream>
using namespace std;
namespace Map
{
template<class K, class V>
class map
{
struct mapofT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, mapofT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, mapofT>::const_iterator const_iterator;
V& operator[] (const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key,V()));
return ret.first->second;
}
pair<iterator,bool> insert(const pair<K,V>& kv)
{
return _t.insert(kv);
}
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin()const
{
return _t.begin();
}
const_iterator end()const
{
return _t.end();
}
private:
RBTree<K, pair<const K, V>, mapofT> _t;
};
}
红黑树
#pragma once
#include<iostream>
#include<assert.h>
#include<utility>
using namespace std;
enum Colour
{
RED,
BLACK
};
template< class T>
class RBTreeNode
{
public:
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _data(data)
{
}
};
template<class T,class Ptr,class Ref>
struct TreeIterator
{
typedef RBTreeNode<T> Node;
typedef TreeIterator<T,Ptr,Ref> self;
typedef TreeIterator<T, T*, T&> Iterator;
Node* _node;
TreeIterator(Node* node)
:_node(node)
{
}
TreeIterator(const Iterator& it)
:_node(it._node)
{
}
Ref operator* ()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator != (const self & s) const
{
return _node!= s._node;
}
bool operator== (const self & s) const
{
return _node == s._node;
}
self& operator--()
{
if (_node->_left)
{
Node* cur = _node->_right;
while (cur->_left)
{
cur = cur->_right;
}
_node = cur;
}
else
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent && parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
self& operator++()
{
if (_node->_right)
{
Node* cur = _node->_right;
while (cur->_left)
{
cur = cur->_left;
}
_node = cur;
}
else
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent)
{
if (cur == parent->_left)
{
break;
}
else
{
cur = cur->_parent;
parent = parent->_parent;
}
}
_node = parent;
}
return *this;
}
};
template<class K, class T,class keyofT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef TreeIterator<T, T*, T&> iterator;
typedef TreeIterator<T,const T*,const T&> const_iterator;
iterator begin()
{
Node* leftmin = _root;
while (leftmin->_left)
{
leftmin = leftmin->_left;
}
return iterator(leftmin);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin()const
{
Node* leftmin = _root;
while (leftmin->_left)
{
leftmin = leftmin->_left;
}
return const_iterator(leftmin);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
Node* find(const T& data)
{
keyofT kot;
Node* cur = _root;
while (cur)
{
if (kot(data) > kot(cur->_data))
{
cur = cur->_right;
}
else if (kot(data) < kot(cur->_data))
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pair<iterator,bool> insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root),true);
}
Node* parent = nullptr;
Node* cur = _root;
keyofT kot;
while (cur)
{
if (kot(data) > kot(cur->_data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(data) < kot(cur->_data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(data) < kot(parent->_data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
Node* uncle = grandparent->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
else//uncle is not or black
{
if (cur == parent->_left)
{
RotateR(grandparent);
grandparent->_col = RED;
parent->_col = BLACK;
}
else
{
RotateL(parent);
RotateR(grandparent);
grandparent->_col = RED;
cur->_col = BLACK;
}
break;
}
}
else//parent == grandparent->_right
{
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
}
break;
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode),true );
}
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
bool is_balance()
{
return is_balance(_root);
}
bool Checknum(Node* root, int blacknum, int stdnum)
{
if (root == nullptr)
{
if (blacknum != stdnum)
{
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blacknum;
}
return Checknum(root->_left, blacknum, stdnum)
&& Checknum(root->_right, blacknum, stdnum);
}
bool is_balance(Node* root)
{
if (root == nullptr)
{
return true;
}
if (root->_col == RED)
{
return false;
}
int stdnum = 0;
Node* cur = root;
while (cur)
{
if (cur->_col == BLACK)
{
++stdnum;
}
cur = cur->_left;
}
return Checknum(root, 0, stdnum);
}
private:
Node* _root = nullptr;
};