STL,全称Standard Template Library(标准模板库),是C++标准库的重要组成部分。
说到C++的STL,让人就不免想到六大组件:
容器
迭代器
算法
仿函数
空间配置器
适配器
从容器开始逐个描述。
容器
容器分为关联式容器和序列式容器
关联式容器
关联式容器使用键来唯一标识元素。
包括set、map、unordered_set、unordered_map。
而这四个又有各自的multi版本,就是允许键值冗余。
set/map 常见接口使用
需要熟练掌握基本的方法,增删改查:
1.insert
set和map的insert都重载了三种方法:单个元素插入,迭代器位置插入,迭代器范围插入
single element (1) pair<iterator,bool> insert (const value_type& val);with hint (2) iterator insert (iterator position, const value_type& val);range (3) template <class InputIterator> void insert (InputIterator first, InputIterator last);
第一种 insert使用set/map的元素类型插入,返回值为一个pair类型,pair的元素为迭代器和bool值
第二种 insert使用迭代器的位置插入 应用场景是先通过迭代器寻找指定元素,找不到时,pair的返回值元素中的bool是false,这时可以在迭代器的位置插入指定元素
第三种 范围插入,从另一个set/map容器中用迭代器选定一个范围,通过插入进新的set/map
2.find
const_iterator find (const value_type& val) const; iterator find (const value_type& val);参数是指定元素,返回值是迭代器
3.erase
(1) iterator erase (const_iterator position);(2) size_type erase (const value_type& val);(3) iterator erase (const_iterator first, const_iterator last);
第一种 是迭代器位置删除,一般需要先将迭代器初始化为需要删除的元素节点,然后直接传入迭代器就可删除( 迭代器会失效,想继续使用这个迭代器需要用返回值更新迭代器)
第二种 是删除指定元素 ,并返回删除元素的数量
第三种 是范围删除,给定一个迭代器的范围,再给特定的一个条件去删除迭代器所指向元素,不过要 注意迭代器失效,要用返回值更新迭代器,才能在接下来的遍历过程中防止迭代器失效
map: operator[]
mapped_type& operator[] (const key_type& k);
operator[]是map独特的一种[ ]重载,参数是键值key,
返回值是map存储的键值对中的 值的引用(mapped_type&)
operator[ ]的用法:
一、可以用来查找[key]位置的值
二、如果[key]为空,可以用来插入新的键值对
三、如果[key]已经存在,可以修改值
iterator
要谈论set和map的迭代器,就必须要先谈它们的底层结构:红黑树
简单看一下红黑树中iterator的定义(自实现):
//红黑树迭代器类
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;__RBTreeIterator(Node* node)
:_node(node)
{}
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}Ref operator*()
{
return _node->_data;
}Ptr operator->()
{
return &_node->_data;
}bool operator!=(const Self& it)
{
return _node != it._node;
}Self& operator++()
{
// 1、右不为空,下一个就是右子树的最左节点
if (_node->_right)
{
Node* subleft = _node->_right;
while (subleft->_left)
{
subleft = subleft->_left;
}
_node = subleft;
}
else// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}_node = parent;
}return *this;
}Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}_node = parent;
}return *this;
}
};
//红黑树类
template<class K, class T, class KofT>
class RBTree
{
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;/*......*/
};
set::iterator
set底层封装了RBTree的结构,这是set的iterator 定义(自实现):
template<class K>
class set{
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;// .........................................................................///
};
注意到这里的迭代器类型本质上都是const类型,因为set只存储const的key(不可修改),为了和底层容器接口保持一致,就把普通迭代器和const迭代器都封装成了const类型。
map::iterator
和set一样封装了BRTree,直接看map中的定义(自实现):
template<class K, class V>
class map{
typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::cosnt_iterator const_iterator;
}
KeyofT
这里要提一下set和map的iterator模版的第三个参数:SetKeyOfT、MapKeyofT
主要是为了让set能够和map共用一个底层红黑树而做的一步,SetKeyOfT和MapKeyofT本质是一个仿函数:
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};struct MapKeyofT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
值得注意的是: 仿函数返回的值是const key&
这说明迭代器在返回key时,不管是const类型还是 非const类型,都会返回const的类型,因为key的性质(需要根据key对节点进行排序)不可修改,只能删除再插入,对map来说修改的是这个key对应的value。
set/map 常见经典问题
1. 底层实现原理:---- 红黑树(待填链接)
2. map的operator[ ] 的返回值是什么? ---- 前面已经提到过: value的引用
3. 一个类型要做map和set的key有什么要求? ---- 支持比较大小,因为底层是搜索树,需要靠大小维护。
4. map和set有什么特点? ---- 查找时间复杂度是log N ,遍历是按key排序的。 set可以对key去重
set/map - multi版本: multiset / multimap
multi版本的map和set和原版区别就是可以允许键值冗余(multimap没有operator [ ]!)