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

来源:互联网 发布:大数据可视化解决方案 编辑:程序博客网 时间:2024/05/17 02:06

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<intint> 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 的构造速度会比较慢,占用内存会比较大。


原创粉丝点击