STL之set和map
set和map都是关联式容器,底层实现都是红黑树,先来看红黑树的实现,明白了红黑树,set和map就很简单了。具体使用在数据结构里面列出,因为做题经常用到。。
红黑树插入和删除操作都是在O(logn)时间内完成的,其他操作建议参考数据结构和算法类的书籍。
rb_tree
一点需要声明的是一个节点中key和data加一起是value.
几个优点,也是为什么set和map要使用这个rb_tree来实现
- 利用平衡二叉搜索树的特性,排列规则有助于search和insert,并保持适度平衡:没有一个节点过深
- 提供遍历以及iterators,按照++iter遍历,能获取排序的装填
- 两种insertion:insert_unique()表示key一定在整个tree中独一无二,否则安插失败。insert_equal()表示节点的key可以重复。
另外:我们不应该使用rb_tree的iterators改变元素值。但是map运行data改变,而key是不可变的。所以并未阻止我们去改变元素值。
部分源码:
template<class Key,class Value,class KeyOfValue,class Compare,class Alloc=alloc>
class rb_tree{
protected:
typedef_rb_tree_node<Value>rb_tree_node;
...
public:
typedef rb_tree_node* link_type;
....
protected://数据,红黑树的类对象本身的大小只是这个三个数据的大小
size_type node_count;//rb_tree的大小(节点数量)
link_type header;//虚拟头节点
Compare key_compare;//key的大小比较准则
};
这只是一个大概,更详细的内容还是多看看源码。
set/multiset
几个需要熟记的点:
- set和multiset都是以rb_tree为底层结构,因此有元素自动排序特性,排序的依据是key,而set和multiset的value和key合一:value就是key。
- 我们无法使用set/multiset的iterators改变元素值,irerator底部是rb_tree的const-iterator,就是为了禁止user对元素赋值
- set和multiset的区别:set的key必须独一无二,因此insert用的是rb_tree的insert_unnique;而multiset的key可以重复,因此其insert()用的是rb_tree的insert_equal。
部分源码:
template<class Key,class Compare=less<Key>,class Alloc=alloc>
class set{
public:
typedef Key key_type;
typedef Key value_type;//
typedef Compare key_compare;
typedef Compare value_compare;
private:
typedef rb_tree<key_type,value_type,identity<value_type>,key_compare,Alloc>rep_type;
rep_type t;//红黑树变量t
public:
typedef typename rep_type::const_iterator iterator;//迭代器是const
....
}
//使用set
set<int>iset;//默认了一部分
set<int ,less<int>,alloc>;
tmplate<int,int,identity<int>,less<int>,alloc>class rb_tree;
map/multimap
几个需要熟记的点:
- map/multimap以rb_tree为底层结构,因此有自动排序的特性,排序的依据还是key
- 允许修改data,不允许修改key,通过将key设定为const类型
- map和multimap的区别:map的key必须独一无二,因此insert用的是rb_tree的insert_unnique;而multimap的key可以重复,因此其insert()用的是rb_tree的insert_equal.
源码:
区别于set。
template<class key,class T,class Compare=less<Key>,class Alloc=alloc>
class map{
public:
typedef Key key_type;
typedef T data_type;//区别于set
typedef T mapped_type;
typedef pair<const Key,T>value_type;//pari键值对,通过这里设置const key来防止修改key,但是可以修改data
typedef Compare key_compare;
private:
typedef rb_tree<key_type,value_type,selectlst<value_type>,key_compare,Alloc>rep_type;
rep_type t;
public:
typedef typename rep_type::iterator iterator;//区分set
}
//使用
map<int,string>imap;//这里是key和data的值
map<int ,string,less<int>,alloc>imap;
template<>;//其实就是使用红黑树
//map中的[]
//可以使用数组的形式来访问数据,当要访问的数据不在map中的时候,会将利用[]的数据以键值对的形式添加到map。
需要清楚的点:
- set,map分别和multiset,multimap的区别:在于元素可不可以重复 ,插入元素的时候是调用inset_unique还是insert_equal
- set的key是通过设置迭代器为const类型来使得不能改变key的值,而Map是通过利用pair类型,来设置key为const类型,使得不能改变key的值,但是可以改变data的值。
- 使用的时候根据具体情况选择合适的容器。
参考:侯捷老师STL讲解
《STL源码刨析》