目录
1、set
1.1、了解set
1.2、set的使用
1、set的模板参数
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
2、set的构造
函数声明 | 函数功能 |
---|---|
set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); | 构造空的set |
set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); | 用[first,last)区间中的元素构造set |
set (const set& x); | set的拷贝构造 |
3、set的迭代器
函数声明 | 函数功能 |
iterator begin(); | 返回set中起始位置元素的迭代器 |
iterator end(); | 返回set中最后一个元素后面的迭代器 |
const_iterator cbegin() const; | 返回set中起始位置元素的const迭代器 |
const_iterator end() const; | 返回set中最后一个元素后面的const迭代器 |
reverse_iterator rbegin(); |
返回set第一个元素的反向迭代器,即end
|
reverse_iterator rend(); |
返回set最后一个元素下一个位置的反向迭代器,即rbegin
|
const_reverse_iterator crbegin() const; |
返回set第一个元素的反向const迭代器,即cend
|
const_reverse_iterator crend() const; |
返回set最后一个元素下一个位置的反向const迭代器,即crbegin
|
4、set的容量
函数声明 | 函数功能 |
bool empty ( ) const;
|
检测set是否为空,空返回true,否则返回false
|
size_type size() const;
|
返回set中有效元素的个数
|
5、set的修改
函数声明 | 函数功能 |
pair<iterator,bool> insert ( const value_type& x );
|
在set中插入元素x,实际插入的是<x, x>构成的 键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>
|
void erase ( iterator position);
|
删除set中position位置上的元素
|
size_type erase (const key_type& x )
|
删除set中值为x的元素,返回删除的元素的个数
|
void erase ( iterator fifirst, iterator last )
|
删除set中[fifirst, last)区间中的元素
|
void swap ( set<Key,Compare,Allocator>&
st );
|
交换set中的元素
|
void clear ( )
|
将set中的元素清空
|
iterator find ( const key_type& x ) const
|
返回set中值为x的元素的位置
|
size_type count ( const key_type& x ) const
|
返回set中值为x的元素的个数
|
2、map
2.1、认识map
2.2、map的使用
1、map的模板参数
key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
2、map的构造
函数构造 | 函数功能 |
map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type()); | 构造空的set |
map (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type()); | 用[first,last)区间中的元素构造map |
map (const map& x); | map的拷贝构造 |
3、map的迭代器
函数声明 | 函数功能 |
iterator begin(); | 返回map中起始位置元素的迭代器 |
iterator end(); | 返回map中最后一个元素后面的迭代器 |
const_iterator cbegin() const; | 返回map中起始位置元素的const迭代器 |
const_iterator end() const; | 返回map中最后一个元素后面的const迭代器 |
reverse_iterator rbegin(); |
返回map第一个元素的反向迭代器,即end
|
reverse_iterator rend(); |
返回map最后一个元素下一个位置的反向迭代器,即rbegin
|
const_reverse_iterator crbegin() const; |
返回map第一个元素的反向const迭代器,即cend
|
const_reverse_iterator crend() const; |
返回map最后一个元素下一个位置的反向const迭代器,即crbegin
|
4、map的容量与元素访问
函数声明 | 函数功能 |
bool empty ( ) const;
|
检测set是否为空,空返回true,否则返回false
|
size_type size() const;
|
返回set中有效元素的个数
|
mapped_type& operator[] (const key_type& k)
|
返回去key对应的value
|
5、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 fifirst, iterator last )
|
删除[fifirst, last)区间中的元素
|
void swap ( map<Key,Compare,Allocator>&
mp );
|
交换map中的元素
|
void clear ( )
|
将map中的元素清空
|
iterator find ( const key_type& x ) const
| 返回map中值为x的元素的位置,找到返回该元素的位置的const迭代器,否则返回cend |
size_type count ( const key_type& x ) const
|
返回值为x的元素的个数
|
3、multiset
3.1、认识multiset
1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成 的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器 中进行修改(因为元素总是const的),但可以从容器中插入或删除。
3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
4. multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
5. multiset底层结构为二叉搜索树(红黑树)。
4、multimap
4.1、认识multimap
5、底层结构
5.1、AVL树
5.1.1、AVL树概念
5.1.2、AVL树节点的定义
template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data = T())
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _bf(0)
{}
AVLTreeNode<T>* _pLeft;
AVLTreeNode<T>* _pRight;
AVLTreeNode<T>* _pParent;
T _data;
int _bf; // 节点的平衡因子
};
5.1.3、AVL的插入
bool Insert(const T& data)
{
if (_pRoot == nullptr)
{
_pRoot = new Node(data);
_pRoot->_bf = 0;
return true;
}
Node* parent = nullptr;
Node* cur = _pRoot;
while (cur)
{
if (cur->_data < data)
{
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > data)
{
parent = cur;
cur = cur->_pLeft;
}
else
{
return false;
}
}
cur = new Node(data);
if (parent->_data > data)
{
parent->_pLeft = cur;
}
else
{
parent->_pRight = cur;
}
cur->_pParent = parent;
while (parent)
{
if (cur == parent->_pRight)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = cur->_pParent;
parent = parent->_pParent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
5.1.4、AVL树的旋转
1. 新节点插入较高左子树的左侧---左左:右单旋
void RotateR(Node* pParent)
{
Node* subL = pParent->_pLeft;
Node* subLR = subL->_pRight;
pParent->_pLeft = subLR;
if (subLR)
{
subLR->_pParent = pParent;
}
Node* ppNode = pParent->_pParent;
subL->_pRight = pParent;
pParent->_pParent = subL;
if (pParent == _pRoot)
{
_pRoot = subL;
_pRoot->_pParent = nullptr;
}
else
{
if (ppNode->_pLeft == pParent)
{
ppNode->_pLeft = subL;
}
else
{
ppNode->_pRight = subL;
}
subL->_pParent = ppNode;
}
subL->_bf = pParent->_bf = 0;
}
void RotateL(Node* pParent)
{
Node* subR = pParent->_pRight;//父节点的右孩子
Node* subRL = subR->_pLeft;//subR的左孩子
pParent->_pRight = subRL;
if (subRL)//若subRL不为空,将subRL的父亲改成pParent
{
subRL->_pParent = pParent;
}
Node* ppNode = pParent->_pParent;//记录父节点的父节点
subR->_pLeft = pParent;//将pParent设为subR的左孩子
pParent->_pParent = subR;//将pParent的父节点设为subR
if (pParent == _pRoot)//如果是根节点
{
_pRoot = subR;
_pRoot->_pParent = nullptr;
}
else
{
if (pParent == ppNode->_pLeft)
{
ppNode->_pLeft = subR;
}
else
{
ppNode->_pRight = subR;
}
subR->_pParent = ppNode;
}
pParent->_bf = 0;
subR->_bf = 0;
}
3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋
void RotateLR(Node* pParent)
{
Node* subL = pParent->_pLeft;
Node* subLR = subL->_pRight;
int bf = subLR->_bf;
RotateL(pParent->_pLeft);
RotateR(pParent);
if (bf == 0)
{
pParent->_bf = 0;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == -1)
{
pParent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 1)
{
pParent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else
{
assert(false);
}
}
4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋
void RotateRL(Node* pParent)
{
Node* subR = pParent->_pRight;
Node* subRL = subR->_pLeft;
int bf = subRL->_bf;
RotateR(pParent->_pRight);
RotateL(pParent);
if (bf == 0)
{
subRL->_bf = 0;
pParent->_bf = 0;
subR->_bf = 0;
}
else if (bf == -1)
{ subRL->_bf = 0;
pParent->_bf = 0;
subR->_bf = 1;
}
else if (bf == 1)
{ subRL->_bf = 0;
pParent->_bf = -1;
subR->_bf = 0;
}
else
{
assert(false);
}
}
5.2、红黑树
5.2.1、概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
5.2.2、性质
5.2.3、红黑树节点定义
// 节点的颜色
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; // 节点的颜色
};
5.2.4、红黑树的插入操作
2. 检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
情况一: cur为红,p为红,g为黑,u存在且为红
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑 p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2
pair<iterator, bool> Insert(const T& data)
{
Node*& pRoot = GetRoot();
if (pRoot == nullptr)
{
pRoot = new Node(data);
pRoot->_parent = _pHead;
pRoot->_color = BLACK;
_size = 1;
return make_pair((iterator)pRoot, true);
}
else
{
KeyOfValue Kof;
Node* cur = pRoot;
Node* parent = nullptr;
while (cur)
{
if (Kof(cur->_data) > Kof(data))
{
parent = cur;
cur = cur->_left;
}
else if (Kof(cur->_data) < Kof(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair((iterator)cur, false);
}
}
cur = new Node(data);
if (Kof(parent->_data) > Kof(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent != _pHead && parent->_color == RED)
{
Node* grandfater = parent->_parent;
assert(grandfater);
if (grandfater->_left == parent)
{
Node* uncle = grandfater->_right;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfater->_color = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// p
// c
RotateR(grandfater);
parent->_color = BLACK;
grandfater->_color = RED;
}
else
{
// g
// p
// c
RotateL(parent);
RotateR(grandfater);
cur->_color = BLACK;
grandfater->_color = RED;
}
break;
}
}
else
{
Node* uncle = grandfater->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfater->_color = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
// g
// p
// c
RotateL(grandfater);
parent->_color = BLACK;
grandfater->_color = RED;
}
else
{
// g
// p
// c
RotateR(parent);
RotateL(grandfater);
cur->_color = BLACK;
grandfater->_color = RED;
}
break;
}
}
}
}
_size++;
pRoot->_color = BLACK;
return true;
}