C++STL:Map剖析
2011-06-05 22:00 x_feng 阅读(796) 评论(0) 收藏 举报map是(键-值)对的集合,也是通常所说的关联数组。可以使用键作为下标来获取一个值,关联的本质在于元素的值与某个特定的键相关联,而并非下标。
1,map简介
map是一类关联容器。它的特点是:增加和删除节点对迭代器影响很小,除了那个操作节点,对其它的节点都没什么影响。对于map来说,不可以修改键,但可以修改键对应值的值。map的底层结构是:平衡二叉搜索树,也就是RB-tree(红黑树)。
功能:
建立key-value的对应。key和value可以是任意类型。
根据key值快速查找记录,时间复杂度为log(N)。
快速插入key-value记录。
快速删除记录。
根据key修改value记录。
遍历。
2,map对象的定义
要使用map必须包含头文件#include <map>。必须指明键和值的类型:
map<string, int>my_map;my_map是一个Map的对象,键的类型是string,值的类型是int,注意map不允许相同的键存在,每个键都是不同的。定义一个map对象有3种方法: map<key, value>m; map<key, value>m2(m); map<key, value>m2(begin, end);//这里的begin和end必须是指向另外一个map的迭代器。由于不能修改键,所以map的每一对记录key-value就是一个pair<const key, value>。
3,map插入数据,insert()函数
定义:map<string, int>my_map;
(1)my_map.insert(map<string, int>::value_type(“haha”, 1) );
(2)my_map.insert(make_pair(“lala”, 1) );
这两种方法都很简便。当然也可以定义一个pair<string, int>p;p.first = “yaya”;p.second = 5;my_map.insert(p)';这样也可以插入。
还有:my_map.insert(beg, end);my_map.insert(iter, e);
map可以用键来访问键对应的值:value = my_map[key];现在看看my_map[“haha”] = 2;会发生什么?
(1)在my_map中查找键haha,如果没有找到
(2)将这个新的键haha插入到my_map中,并对键haha初始化为0
(3)读取新插入的键haha,并将它的值赋为2
假如说我们想统计一篇文章的单词,并记录每个单词出现的次数,下面实现这个惊人的简练:
map<string, int>word_count;
string word;
while(cin >> word)
++word_count[word];//当然这样做效率不高,一次操作单词被查询2次。
那么insert()的返回值是什么呢?
my_map.insert(pair<string, int>);这种带有pair类型的参数返回一个pair型对象,pair的第一个值是my_map当前的迭代器,第二个值是bool,如果这个string已经存在了则返回false表示没有插入,这个key已经存在了,也不会修改key对应的value值。否则返回true表示插入成功。
现在我们再来看看上面的统计单词次数,为了提高效率,我们如下做:
map<string, int>word_count;
string word;
while(cin >> word)
{
pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
if (!ret.second)//返回false表示键已有了,
++ret.first->second;//让这个单词的次数再加1
}//这样我们就判断了一次
4,map查找数据,count(),find()函数
map<string, int>my_map; my_map.count(key);//查找这个键在不在,如果在就返回1,没有就返回0,这个函数只返回这两个值0或1 my_map.find(key);//查找这个键是否存在,如果有返回指向的key的迭代器,否则返回my_map.end()。
5,map删除数据,erase()函数
从map容器中删除元素的erase()操作有3种变化形式。与顺序容器一样,可向erase()传递一个或一对迭代器,来删除单个元素或一段范围内的元素。但有点不同:map容器的erase()操作返回void,而顺序容器的erase()操作返回一个迭代器,指向被删除元素后面的元素。
map<string, int>my_map; my_map.erase(key);//通过关键字删除,这一种返回值是 key的个数,对于map是0或1 my_map.erase(iter);//通过迭代器删除,返回void my_map.erase(beg, end);//通过迭代器删除一段范围,返回void if (my_map.erase(key))//可以判断这个key是否存在,存在返回1,不在返回0
6,map底层数据结构
在说这个之前我们看看AVL tree:平衡二叉搜索树。满足一下条件:
1.任意节点大于它的左子树节点值,小于右子树节点值
2.任意节点的左右子树高度相差最多1。如下图:
与AVL-tree相似,另一被广泛使用的就是RB-tree树:平衡二叉搜索树(也称作红黑树)。除了满足AVL-tree的条件,还要满足一下规则:
1,每个节点不是红色就是黑色。
2,根节点为黑色。
3,如果节点为红,其子节点必须为黑。
4,任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同。如下图:
其实map的底层就是RB-tree。
注:此图截取至《STL源码剖析》,如果你对map的底层结构感兴趣,可以详细查看这本书。


浙公网安备 33010602011771号