您的位置:首页 > 编程语言 > C语言/C++

C++联合容器set和map

2014-07-29 15:43 399 查看
联合容器

联合容器将值与关键字关联在一起,使用关键字来查找值。联合容器的长处在于,它提供了对元素的快速访问。与序列相似,联合容器也允许插入新元素,不过不能指定元素的插入位置。原来是联合容器通常包含用于确定数据放置位置的算法,以便能够很快检索信息。

STL提供4种容器:set,multiset,map,multimap。一般地当我们只想知道一个值是否存在时,set 最有用处;希望存储也可能修改一个相关的值时,map 最为有用。

set
STL set是多个概念的模型,它是一个联合集合,可反转,可排序,关键字是唯一的,所以它只能存储同一种类型的值。其值的类型与关键相同,关键字是唯一的,对于set而言,值就是关键字。头文件为<set>。
定义set
set<string> A(s1, s1+A)
set<string> exclusion_set;


插入
exclusion_set.insert( "the" );
exclusion_set.insert( "and" );

我们可以通过向
insert()
提供一对iterator 以便插入一个元素序列
查找

查询set 对象中是否存在一个值的两个操作是
find()
count()
。如果元素存在则
find()
返回指向这个元素的iterator,否则返回一个等于
end()
的iterator表示该元素不存在。如果找到元素
count()
返回1
如果元素不存在则返回0。
迭代
set< short > occurrence_lines;
ploc::iterator liter = ploc->begin(),
ploc::iterator liter_end = ploc->end();
while ( liter != liter_end ) {
occurrence_lines.insert( occurrence_lines.end(),
(*liter).first );
++liter;
}


map
对于map类型来说,值的类型与关键字不同,关键字是唯一的,每个关键字只对应一个值。
为定义map对象我们至少要指明键和值的类型,例如
map<string, int> word_count;
class employee;
map<int, employee*> personnel;


加入键/值元素对
word_count[ string("Anna") ] = 1;


用下标操作符把map初始化至一组元素集合,会使每个值都被初始化为缺省值。然后再被赋值为显式的值,如果元素是类对象而且它的缺省初始化和赋值的运算量都很大,就会影响程序的性能。可以直接插入:
word_count.insert(map<string,int>::value_type( string("Anna"),
1 ));


或者
typedef map<string,int>::value_type valType;
word_count.insert( valType( string("Anna"), 1 ));

其作用是创建一个pair对象,接着将其直接插入map。

为插入一定范围内的键/值元素,我们可以用另一个版本的insert()方法。它用一对iterator作为参数。
map< string, int > word_count_two;
word_count_two.insert(word_count.begin(),word_count.end());


查找并获取map 中的元素

下标操作符给出了获取一个值的最简单方法例如:
int count = word_count[ "wrinkles" ];


但是只有当map 中存在这样一个键的实例时该代码才会表现正常如果不存在这样的实例使用下标操作符会引起插入一个实例在本例中键/值对
string(
"wrinkles" ), 0


有两个map 操作能够发现一个键元素是否存在而且在键元素不存在时也不会引起插入实例:

(1) count(keyValue)

返回map 中keyValue 出现的次数当然对于map而言返回值只能是0 或1。
int count = 0;
if ( word_count.count( "wrinkles" ))
count = word_count[ "wrinkles" ];

(2) Find(keyValue) 如果实例存在则find()返回指向该实例的iterator,如果不存在则返回等于end()的iterator。例如
int count = 0;
map<string,int>::iterator it = word_count.find( "wrinkles" );
if ( it != word_count.end() )
count = (*it).second;


对map进行迭代

我们可以通过对由begin()和end()两个迭代器标记的所有元素进行迭代来做到这一点。指向map中元素的iterator指向一个pair对象其中first拥有键,second拥有值。
typedef map<string,int> tmap;
tmap::iterator iter = text_map->begin(),
tmap::iterator iter_end = text_map->end();
while ( iter != iter_end )
{
cout << "word: " << (*iter).first << " (" << (*iter).second <<")";
iter ++;
}


删除

从map 中删除元素的erase()操作有三种变化形式。为了删除一个独立的元素我们传递给erase()一个键值或iterator, 为了删除一列元素我们传递给erase()一对lieator。

multiset和multimap
multiset与set类似,但前者有多个值的关键字相同。multimap类型与map相似,只是一个关键字可以与多个值关联。
声明:
# include <map>
multimap< key_type, value_type > multimapName;
multimap< string, list< string > > synonyms;
# include <set>
multiset< type > multisetName;

multimap<int, string> codes;


int表关键字类型,string是存储值的类型。可选的第三个参数是比较函数,默认用less<>。
插入

为将关键字和值结合为一对,定义了
pair<class T, class U>
,如果keytype是关键字类型,datatype是被存储的数据类型,则值类型为
pair<const
keytype, datatype>


举例:
pair<const int, string> item(213, "Los Angeles");
code.insert(item);

或者创建一个匿名的pair对象,
code.insert( pair<const int, string>(213, "Los Angeles") )


有了pair对象后,可以使用first和second来访问这两个部分,如
item.first,item.second

其它接口

对于multimap 或multiset ,一种迭代策略是联合使用由find()返回的iterator (指向第一个实例)和由count()返回的值.这样做能奏效因为我们可以保证实例在容器中是连续出现的。
multimap< string, string > authors;
string search_item( "Alain de Botton" );
// ...
int number = authors.count( search_item );
multimap< string,string >::iterator iter;
iter = authors.find( search_item );
for ( int cnt = 0; cnt < number; ++cnt, ++iter )
do_something( *iter );

更精彩的策略是使用由multiset 和multimap 的特殊操作
equal_range()
返回的iterator
对值。如果这个值存在,则第一个iterator 指向该值的第一个实例。且第二个iterator 指向这个值的最后实例的下一位置。如果最后的实例是multiset 的末元素,则第二个iterator 等于
end()

typedef int Keytype;
multimap<int, string> codes;
pair<multimap<Keytype, string>::iterator, multimap<Keytype, string>::iterator> range=code.equal_range(718)
for(it=range.first; it!=range.second;++it)
cout<<(*it).second<<endl;

插入和删除操作与关联容器set 和map 相同equal_range()可以用来提供iterator 对以便标记出要被删除的多个元素的范围。

其它成员函数包括
lower_bound()
upper_bound()
将关键字作为参数。

内部数据结构

C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般的平衡二叉树。这也就能解决以下几个问题

(1)为何map和set的插入删除效率比用其他序列容器高?

很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。map和set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。

(2)为何每次insert之后,以前保存的iterator不会失效?

因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点就OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然 被删除的那个元素本身已经失效了)。

(3)map和set的插入和查找速度变化如何?

在map和set中查找是使用二分查找,插入和查找速度为logN。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ STL 联合容器