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

C++学习笔记2.2 : 容器和算法之关联容器

2013-12-19 13:38 567 查看
1. 关联容器和顺序容器:p305

l 关联容器:关联容器通过键(Key)存储和读取元素。(按顺序存储—键值的大小顺序)

n 关联容器的基础:pair 类型。

n 顺序存放,但是不按其顺序/位置访问,而是按其键值(key)访问元素。按键值访问的话,可确保按键的顺序访问元素。

n map : 元素以键-值(key-value)对的形式组织;键用作元素在 map 中的索引,而值则表示所存储和读取的数据。

n set : 所含元素仅包含一个键,并有效地支持关于某个键是否存在的查询。

l 顺序容器:顺序容器通过元素在容器中的位置顺序存储和访问元素。



2. 关联容器的类型 p306

l map : 关联数组,元素通过键来存储和读取。 (#include <map>)

l set : 大小可变的集合,支持通过键实现的快速读取。 (#include <set>)

l multimap : 支持同一键多次出现的 map 类型。 (#include <map>)

l multiset : 支持同一个键多次出现的 set 类型。 (#include <set>)



3. pair类型 p306

l pair 类型在 utility 头文件定义。(#include <utility>)

l pair 类中的数据成员都是公有的,可以直接访问:first 成员 和 second 成员。

l pair 类型提供的操作: (T1 和 T2 类型可以不同)

n pair<T1, T2> p1; 创建一个空的pair 对象,它的两个元素分别为 T1 类型和T2 类型的,采用值初始化。

u pair<string, string> author("james", "joycs"); // 定义一个pair 对象,包含两个 string 类型成员。

u pair<string, vector<int> > line; // 定义一个pair对象,存储一个空的string类型和一个空的 vector 类型对象。

u 可以使用 typedef 来简化 pair 的声明:

l typedef pair<string, string> author;

l author Prous("marry", "jack");

n pair<T1, T2> p1(v1, v2); 创建一个 pair 对象,它的两个元素为 T1 和 T2 类型,其中 first 成员初始化为 v1,second 成员初始化为 v2。

n make_pair(v1, v2); 以 v1 和 v2 值创建一个新的 pair 对象,其元素类型分别是 v1 和 v2的类型。操作返回 pair 类型,一般用于右值给pair 变量赋值。

n p1 < p2; 两个 pair 对象之间的小于运算,其定义遵循字典次序,只有两个 pair 对象的的两部分都满足小于条件时,返回 true。

n p1 == p2; 如果两个 pair 对象的 first 和 second 成员依次相等,则这两个对象相等。该运算使用其元素的 == 操作符。

n p.first :返回 p 中名为 first 的(公有)数据成员。

n p.second : 返回 p 中名为 second 的(公有)数据成员。



4. 关联容器和顺序容器的公共操作

l 关联容器共享顺序容器的大部分操作,但是关联容器不提供 front()、push_front()、pop_front()、back()、push_back() 以及 pop_back() 操作。

l 关联容器支持的公共操作:

n p265描述的三种构造函数(不包括只提供一个容器大小值的构造函数)

u C<T> c; // 创建一个空的关联容器对象。

u C<T> c1(c2) // 创建一个管理容器,以关联容器 c1 为副本。(c1 和 c2 的类型必须相同)

u C<T> c(b, e) // 创建关联容器,并复制迭代器 b 和 e 指向的区间内容为关联容器内容。

n p277描述的关系运算

n p273列出的 begin、end、rbegin、rend操作。

n p272列出的类型名,注意 map 容器,value_type 并非元素的类型,而是一个 pair(const key, value) 类型。key 的类型是const 的,因为 map 中,键值是不可改的。

n p283描述的 swap 和赋值操作,但是关联容器不提供 assign 操作。

n p280列出的 clear 和 erase 操作,但是关联容器的 erase 运算返回 void 类型(顺序容器的 erase 操作返回,指向被删除元素下一位置的迭代器)

n p278列出的关于容器大小的操作,但是关联容器不能使用 resize 函数。



5. map 类型 #include <map>

l map 类型是按顺序存储的。(键值的大小顺序)

l map 的实现是以基于“红黑树”作为其底层数据结构来实现的。

l 使用 map 对象,需包含 map 头文件。

l map 的构造函数:

n map<k, v> m; //创建一个名为 m 的空 map 对象,其键和值的类型分别为 k 和 v。

n map<k, v> m(m2); //创建 m2 的副本,m 与 m2 必须有相同的键类型和值类型。

n map<k, v> m(b, e); //创建 map 类型对象m,存储迭代器 b 和 e 标记的范围内所有元素的副本。元素的类型必须能转化为 pair<const k, v> 类型。

l map 键值需满足的条件:

n 一种数据类型若作为 map 类型的键值,必须满足标准库定义的“<”操作,并能使用该操作进行键值的比较。



6. map 定义的类型

l map 对象的元素时键-值对,元素类型value_type 为 pair 类型,而且键为 const 类型。

n map<K, V> :: key_type 在map 容器中,做索引的键的类型。

n map<K, V> :: mapped_type 在 map 容器中,键值所关联的值的类型。

n map<K, V> :: value_type 一个 pair 类型,它的first 元素具有 const map<K, V>::key_type 类型,而second 元素则为 map<K, V> :: mapped_type 类型。



7. map 类型添加元素

l 使用“下标”访问 map 元素。

n map 的下表运算返回值为,该下标键所关联的值(map<k, v> :: mapped_type类型

n 若该下标值对应的键-值对不存在,则会向 map 中添加该键-值对。同时将对该键-值对的值元素进行相关的初始化。

n map 下标操作符返回的类型与对 map 迭代器进行解引用获得的类型不相同:

u map 下表操作返回该下标键对应的关联值--- map<const k, v>::mapped_type类型。

u map 迭代器返回的类型为value_type类型--- pair<const k, v>类型。

l 使用 insert 成员访问 map 元素。 (map :: insert)

n map 的 insert 成员与顺序容器类似,但是必须考虑键的作用(参数必须关联到 pair 对类型)

u m.insert(e); // e 是一个用在 m 上的 value_type 类型的值,如果键(e.first)不在 m 中,则插入一个值为 e.second 的新元素;如果该键在 m 中已存在,则保持 m 不变,该函数返回一个 pair 类型对象,包含指向键为e.first 的元素的 map 迭代器,以及一个 bool 类型的对象,表示是否插入了该元素。返回值:pair<map<k,
v>::iterator, bool>型,e为pair<const k, v>型。


u m.insert(beg, end); // beg 和 end 是标记元素范围的迭代器,其中的元素必须是 m.value_type类型的键-值对。对于该范围内的所有元素,如果它的键在 m 中不存在,则将该键及其关联的值插入到 m,返回 void 类型。

u m.insert(iter, e); // e是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则创建新元素,并以迭代器 iter 为起点搜索新元素存储的位置。返回一个迭代器,指向 m 中具有给定键的元素。

n 应使用 insert 代替 map 的下标运算:

u 使用 insert 成员可避免使用下表操作符所带来的副作用:不必要的初始化(当下表操作的键值对不存在时,会添加新元素,并发生相关的键值对的初始化)

n 使用 make_pair(v1, v2) 来简化 insert 成员的实参传递:

u m.insert(make_pair(const k, v)); // 例:counr.insert(make_pair("jame", 1));

n 也可使用 typedef 来完成相似的功能:

u typedef map<string, int>::value_type valType;

u count.insert(valType("jame", 1));

l 带有一个键-值 pair 形参的insert的返回类型(m.insert(e)):

n 返回一个pair类型:

u 包含一个map<k, v>::iterator 迭代器,指向具有给定键值的元素(无论插入操作是否成功)。

u 包含一个 bool 类型:表示是否插入了该元素值:

l false:该键已在容器中,map 中的内容不变,不执行insert 操作。

l true:该键不在容器中,则插入该新元素。



8. 查找并读取 map 中的元素:(读取元素而又不插入该元素

l m.count(k) 返回 m 中 k 的出现次数。(返回值只能为 0 或 1,因为 map 中不存在重复的键值)

n 只能检查 元素 是否存在,不能通过返回来获得该元素,即无法得到该元素的位置

l m.find(k) 如果 m 容器中存在按键 k 索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端迭代器。

n 返回指向元素的迭代器,如果元素不存在,则返回 end 迭代器。

n 不仅能够通过返回值判断某元素是否存在,还能通过返回值获得该元素的迭位置(迭代器)。

n 注意:map 的迭代器是一个 pair 对类型。

l map 中的解引用操作:

n map<string, int>::iterator map_it = map.find( "the" ) ;

n cout << map_it -> first ; // 输出 map 中该元素的键值

n cout << map_it -> second ; // 输出 map 中该元素的键值对应的关联值。



9. 从 map 对象中删除元素

l map 中使用参数为一个迭代器的 erase 操作删除元素,但是返回值与顺序容器的 erase 操作不同

n map 容器的单迭代器参数的 erase 操作返回 void。

n 顺序容器的单迭代器参数的 erase 操作返回一个迭代器,指向被删除元素后面的元素。

l map 的三种 erase 操作:

n m.erase(k) 删除 m 中键为 k 的元素。返回 size_type 类型的值,表示删除的元素个数

n m.erase(p) 从 m 中删除迭代器 p 所指向的元素,p 必须指向 m 中确实存在的元素,而且不能等于 m.end()。返回 void 类型

n m.erase(b, e) 从 m 中删除一段范围内的元素,该范围由迭代器 b 和 e 标记。b 和 e 必须标记 m 中的一段有效范围:即 b 和 e 都必须指向 m 中的元素或最后一个元素的下一位置。而且,b 和 e 要么相等(此时删除的范围为空),要么 b 所指向的元素必须出现在 e 所指向的元素之前。返回 void 类型。



10. set 类型(#include <set>

l set 类型是按顺序存储的。(键值的大小顺序)

l 在 STL 中,set 类型是以“红黑树”为底层数据结构来实现的

l map 容器是键-值对的集合,而 set 容器只是单纯的键的集合。

l set 容器存储的键也必须唯一,不能修改(const k)

l set 不支持的 map 操作

n set 不支持“下表操作符”。

n set 中没有定义 mapped_type 类型。(set 容器中 value_type 不是 pair 类型,而是与 key_value 相同的类型。)

l set 支持的 map 操作:

n 4 中的关联容器和顺序容器的公有操作。

n map 支持的三种构造函数。(set 支持使用 vector 等顺序容器进行初始化

u set<int> iset(ivec.begin(), ivec.end()); //若 ivec 中存在重复元素,则重复的内容只被添加一次!

n map 支持的insert 操作

n map 支持的 count 和find 操作

n map 支持的 erase 操作。



11. 在 set 中添加元素

l 使用insert 成员在 set 中添加元素。(set 不支持“下表操作符”

n set<string> set1;

n set1.insert("the"); // 使用 s.insert(e) 版本的 insert 操作。返回 pair 类型(与 map 容器一致)

u 使用带有一个参数的 insert 版本返回 pair 类型对象:

l 包含一个迭代器:指向拥有该键的元素

l 包含一个 bool 值:表明是否添加了元素

n set1.insert(ivec.begin(), ivec.end() ); // 使用 s.insert(b, e) 版本的 insert 操作,返回 void 类型



12. 在 set 中获取元素

l set 中的键值为 const :不支持修改 set 的键值。

l 可以使用 find 或 count 运算在 set 中获取元素。(set 不支持“下表操作符”

n set 使用 find 运算:返回指向该元素的迭代器(返回s.end() 表示该元素不存在)

u set 中的键值不可改,获得的迭代器只可用于读,不可用于 写。

u iset.find(1);

u iset.find(11);

u set<int>::iterator set_it = iset.find();

u *set_it = 11; // Error : 不能修改 set 中的键值。

n set 使用 count 运算:返回 set 中该键对应的元素个数。

u iset.count(1);l

u iset.count(11);



13. multimap 和 multiset 类型 (#include <map> / #include <set>)

l multimap 和 multiset 与 map 和 set 一样都是按顺序存储的。(键值大小的顺序)

n multimap 和 multiset 中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。

n 迭代遍历 multimap 或 multiset 容器时,可保证一次返回特定键所关联的所有元素。

n 在 multimap 和 multiset 容器中,同一键所关联的元素必然相邻存放

l multimap 和 multiset 所支持的操作分别于 map 和 set 的操作相同,只有一个例外: multimap 不支持 “下表运算”

l 在使用 multimap 或 multiset 时,对于某个键,必须做好处理多个值的准备。



14. 在 multimap 和 multiset中添加和删除元素

l insert(添加)和 erase(删除)操作依然适用于 multimap 以及 multiset 容器。

l insert 操作添加元素:

n 每次调用 insert 总会添加一个元素,因为容器中键值不要求是唯一的。

u 返回类型同 map 和 set 中对应的返回类型。

u author.insert(make_pair(string("barth"), string("james") ) ) ; // OK,成功执行

u author.insert(make_pair(string("barth"), string("bron") ) ) ; // OK,成功执行(添加重复键)

l erase 操作删除元素:

n 带有一个键参数的 erase 版本将删除拥有该键的所有元素,并返回删除元素的个数。

n multimap<string, string> authors;

n string search_item("Kazuo Ishiguro") ;

n multimap<string, string>:: size_type cnt = author.erase(search_item) ; // OK,返回删除元素个数。

n 带有一对迭代器参数的 erase 版本只删除制定范围内的元素,并返回 void 类型。



15. 在 multimap 和 multiset 中查找元素

l multimap 和 multiset 中同一个键所关联的元素必然相邻存放。

n 使用 find 和 count 操作查找元素:

u count 函数求出某键出现的次数。

l string search_item("Alain de Botton");

l multimap<string, string>::size_type sz_type = author.count( search_item ) ;

u find 操作返回一个迭代器,指向第一个拥有查找键的实例

l multimap<string, string>::iterator iter = author.find( search_item ) ;



16. lower_bound 和 upper_bound 操作(还有 equal_range 操作)

l lower_bound 和 upper_bound 操作适用于所有的关联容器

l 输入:一个键值(需要查找元素键值)

l 输出:一个迭代器(指向满足键值要求的元素,返回 end() 表明该元素不存在)

l m.lower_bound(k) 返回一个迭代器,指向键不小于 k 的第一个元素。(键值为 k 的第一个元素—下界

l m.upper_bound(k) 返回一个迭代器,指向键大于 k 的第一个元素。(键值为 k 的最后一个元素的下一位置--- 上界的下一位置

l m.equal_range(k) 返回一个迭代器的 pair 对象

n 其返回值为:迭代器的 pair 对象:pair< multimap<T1, T2>::iterator, multimap<T1, T2>::iterator >型

u 它的 first 成员等价于 m.lower_bound(k)。

u 它的 second 成员等价于 m.upper_bound(k)。

l 若查找的元素不在容器中,则上述三种方法返回的迭代器内容都指向,依据元素的排列顺序该键应该插入的位置。(若需要插入该元素,可利用此返回结果)



17. hash_map 与 map 类型

l hash_map 类型:(#include <hash_map>

n hash_map 是一个聚合类:

u 继承自 _Hash 类,

u 包含一个 vector:用于保存桶。

u 一个list :用于进行冲突处理。

u 一个 pair :用于保存 key-value 结构。

n hash_map 结构伪代码:

l class hash_map<class _Tkey, class _Tval>
l {
l private:
l typedef pair<_Tkey, _Tval> hash_pair;
l typedef list<hash_pair> hash_list;
l typedef vector<hash_list> hash_table;
l };

n hash_map 使用示例:

l hash_map<int, int> IntHash;
l IntHash[1] = 123;
l IntHash[2] = 456;
l int val = IntHash[1];
l int val = IntHash[2];

l 构造函数的不同:

n hash_map 类型约束:需要 hash 函数,等于函数;map 只需要比较函数(小于函数)。

l 存储结构不同:

n hash_map 采用 hash 存储,map 一般采用“红黑树(RB Tree)”实现,因此其数据结构是不一样的。

l 什么时候需要 hash_map ,什么时候需要 map ?

n 总体来说,hash_map 的查找速度比 map 快:

u hash_map 的查找速度与数据量(n)无关,属于常数级别。

u map的查找速度是 log(n) 级别(由树的实现结构决定)

u 但是 hash_map 中存在 hash 函数的执行时间,map中没有额外的函数执行时间。

n 当数据量比较大时 hash_map 的构造速度会比较慢,占用内存会比较大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: