map&set
来源:互联网 发布:水滴桌面软件 编辑:程序博客网 时间:2024/05/18 01:42
STL中的map和set底层都是用红黑树实现的关联式容器。两者都是对红黑树做的封装去实现的。
set(集合)
set是一种key结构,key就是value,二者等价,进入set的数据会自动排序。不允许键值重复,无法通过迭代器去改变set的值。
template <class Key, class Compare = less<Key>, class Alloc = alloc>class set {public: // typedefs: 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; // red-black tree representing set };
通过set的成员可以明显看出,其底层是红黑树,并且kye和value是同一个东西。
看下迭代器部分的声明
public: typedef typename rep_type::const_pointer pointer; typedef typename rep_type::const_pointer const_pointer; typedef typename rep_type::const_reference reference; typedef typename rep_type::const_reference const_reference; typedef typename rep_type::const_iterator iterator; typedef typename rep_type::const_iterator const_iterator; typedef typename rep_type::const_reverse_iterator reverse_iterator; typedef typename rep_type::const_reverse_iterator const_reverse_iterator; typedef typename rep_type::size_type size_type; typedef typename rep_type::difference_type difference_type;
set的迭代器是用红黑树的迭代器去封装的,并且是const迭代器,显然通过迭代器去修改set的值是无法实现的。
基本的结构就是这样,下面去认识一些常用的接口。
insert()
提供了三个接口:
//指定元素插入 返回值为pair 第二个参数bool值可以判断元素是否插入成功pair<iterator,bool> insert (const value_type& val);//指定位置插入,但位置可能是不合适的,会导致插入失败,插入成功返回新位置的迭代器iterator insert (iterator position, const value_type& val);//指定迭代器区间插入template <class InputIterator> void insert (InputIterator first, InputIterator last);
erase()
// 指定位置删除void erase (iterator position);//指定元素删除 删除成功返回1size_type erase (const value_type& val);//删除一段迭代器区间void erase (iterator first, iterator last);
set是对红黑树进行封装来实现的,为了能更加深刻认识这点,让我们来看下set的内部实现
// 擦除指定位置的元素, 会导致内部的红黑树重新排列void erase(iterator position){ typedef typename rep_type::iterator rep_iterator; t.erase((rep_iterator&)position);}// 会返回擦除元素的个数, 其实就是标识set内原来是否有指定的元素size_type erase(const key_type& x){ return t.erase(x);}// 擦除指定区间的元素, 会导致红黑树有较大变化void erase(iterator first, iterator last){ typedef typename rep_type::iterator rep_iterator; t.erase((rep_iterator&)first, (rep_iterator&)last);}
可以明显看出,其自身并没有做什么过多的事情。
clean()
清除函数 同样是调用红黑树的clean();
find()
红黑树也进行了实现,直接调用即可
count()
查找指定元素的个数,我们明显可以看出这个接口对于set来说有点鸡肋了,不允许重复它的个数不是0就是1,显然就是存在或不存在。
它存在的意义在于与multiset(允许键值重复)相对应。
其他操作函数
// 返回小于当前元素的第一个可插入的位置iterator lower_bound(const key_type& x) const{ return t.lower_bound(x);}// 返回大于当前元素的第一个可插入的位置iterator upper_bound(const key_type& x) const{ return t.upper_bound(x);}// 返回与指定键值相等的元素区间pair<iterator,iterator> equal_range(const key_type& x) const{ return t.equal_range(x);}
map (kv结构)
map的增删查改与set是类似的,这里就不多做赘述了,map着重认识一个运算符重载函数 operator[];
map是kv结构,也就是一个key值对应一个value.显然可以用这个结构去统计次数。
给定下列的场景,统计水果出现的次数。
方案1:
#include <iostream>#include <map>#include <string>using namespace std;int main(){ string fruit[] = { "apple", "pair", "banana", "apple", "pair", "apple" }; map<string, int> countmap; for (size_t i = 0; i < 6; ++i) { map<string, int>::iterator ret = countmap.find(fruit[i]); if (ret != countmap.end()) { ret->second++; //(*ret).second++; } else { countmap.insert(pair<string, int>(fruit[i], 1)); } } return 0;}
第一种方法先查找,然后判断,在直接++,否则插入。
方案2:
#include <iostream>#include <map>#include <string>using namespace std;int main(){ string fruit[] = { "apple", "pair", "banana", "apple", "pair", "apple" }; map<string, int> countmap; for (size_t i = 0; i < 6; ++i) { pair<map<string, int>::iterator, bool> ret = countmap.insert(make_pair(fruit[i], 1)); if (ret.second == false) { ++(ret.first->second); } } return 0;}
方案利用了map不允许键值重复的特性,如果重复,那么返回指向出现该键值的迭代器,和为false的bool值两者组成的pair。
方案三:
#include <iostream>#include <map>#include <string>using namespace std;int main(){ string fruit[] = { "apple", "pair", "banana", "apple", "pair", "apple" }; map<string, int> countmap; for (size_t i = 0; i < 6; ++i) { countmap[fruit[i]]++; } return 0;}
方案三用一句代码搞定了之前的所有事情,它呢用了我们今天的主角,operator[];
operator[]究竟做了什么,让我们来看下operator[]的实现.
T& operator[](const key_type& k) { return (*((insert(value_type(k, T()))).first)).second; }
stl的实现显然很有水准,让我们分开来看
insert( value_type( k, T() ) ) 这部分先记为V
先调用插入函数,插入的是一个用key值以及T()的默认构造初始化的value;
return (* ( (V).first ) ).second;
然后取出V.first 即是指向元素的迭代器(指向value_field 的指针)
接着呢对调用迭代器的 operator* 会取出 value_field pair<key,value>
最后拿到pair.second 即value
分析玩代码,可以看出,这就相当于一个方案二的变种。
operator[] 会拿到当前key值对应的value,并且还是value的引用,显然就意味着你可以通过调用 operator[] 对当前key值的value进行修改。
备注:
multimap 显然是不会实现operator[] 的,因为无法确定拿出key值对应的那个value。
- set, map
- map&set
- map&set
- set&&map
- HDU4585-map/(map+set)
- List,set,Map
- Set、List、Map区别
- Set List Map总结
- List,Map,Set,容器
- 关于map 和set
- map list set 区别
- List , Map . Set 类
- 关于set,list,map
- Set,Map,List
- JAVA---List,Map,Set
- std::map&&std::set
- map set list 区别
- List Set Map
- 算法导论16.2-1
- Idea添加JQuery库-解决jsp页面使用jQuery方法时显示Unresolved function or method
- Java提高篇——通过分析 JDK 源代码研究 Hash 存储机制
- Git详解
- FCC算法:十、猴子吃香蕉, 分割数组--Chunky Monkey
- map&set
- C或C++编译成lib或dll时修饰符extern C及相关的浅析
- JS 中 == 和 === 的区别
- 5.0 引用类型
- UVA
- POj 2235 Wireless Network
- 算法爱好者——颜色分类 ? 待解决
- 关于tomcat启动日志的选择性打印和禁止打印的问题
- Codevs4189 字典