大纲 set 与 multiset 容器 set 与 multiset 容器的概念 set 容器的概念 set 是一个集合容器,其中所包含的元素是唯一的,集合中的元素会自动按一定的顺序排列。 元素插入过程是按排序规则插入,所以不能指定插入位置。set 的元素不像 map 那样可以同时拥有实值和键值,set 的元素即是键值又是实值。Set 不允许两个元素有相同的键,也不可以通过 set 的迭代器改变 set 元素的值,因为 set 元素值就是其键值,关系到 set 元素的排序规则。如果任意改变 set 元素值,会严重破坏 set 的组织。换句话说 set 的 iterator
是一种 const iterator
。set 拥有和 list 某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。
multiset 容器的概念 multiset 的特性及用法和 set 完全相同,唯一的区别在于它允许元素重复。 set 和 multiset 的底层实现都是红黑树,而红黑树是平衡二叉树的一种。二又树就是任何节点最多允许有两个字节点,分别是左子结点和右子节点,如下图所示:
总结
set 采用红黑树变体的数据结构实现,红黑树属于平衡二叉树,在插入操作和删除操作上比 vector 快。 set 不可以直接存取元素,即不可以使用 at.(pos)
函数和 []
操作符。 multiset 与 set 的区别:set 支持唯一键值,每个元素值只能出现一次;而 multiset 中同一值可以出现多次。 不可以直接修改 set 或 multiset 容器中的元素值,因为这类容器是自动排序的,如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。 set 与 multiset 容器的使用 set 与 multiset 的构造与赋值 默认构造函数 1 2 3 4 5 6 7 set<int > setInt; set<float > setFloat; set<string> setString; multiset<int > mulsetInt; multi set<float > multisetFloat; multi set<string> multisetString;
赋值操作说明 1 2 3 4 5 6 7 set (const set &st); set& operator =(const set &st); set.swap (st); multiset (const multiset &mst); multiset& operator =(const multiset &mst); multiset.swap (mst);
构造与赋值的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> #include <set> using namespace std;void printSet (set<int > &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } void printMultiSet (multiset<int > &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } int main () { cout << "------ set 构造函数 ------" << endl; set<int > s1; s1.insert (5 ); s1.insert (2 ); s1.insert (9 ); s1.insert (4 ); s1.insert (3 ); printSet (s1); set<int > s2 = s1; printSet (s2); cout << "------ set 赋值操作 ------" << endl; set<int > s3; s3 = s1; printSet (s3); set<int > s4; s4.insert (10 ); s4.insert (30 ); s4.insert (20 ); s4.swap (s1); printSet (s1); printSet (s4); cout << "------ multiset 构造函数 ------" << endl; multiset<int > s5; s5.insert (5 ); s5.insert (2 ); s5.insert (2 ); s5.insert (3 ); s5.insert (3 ); printMultiSet (s5); multiset<int > s6 = s5; printMultiSet (s6); cout << "------ multiset 赋值操作 ------" << endl; multiset<int > s7; s7 = s5; printMultiSet (s7); multiset<int > s8; s8.insert (10 ); s8.insert (30 ); s8.insert (20 ); s8.swap (s5); printMultiSet (s5); printMultiSet (s8); return 0 ; }
程序运行输出的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ------ set 构造函数 ------ 2 3 4 5 9 2 3 4 5 9 ------ set 赋值操作 ------ 2 3 4 5 9 10 20 30 2 3 4 5 9 ------ multiset 构造函数 ------ 2 2 3 3 5 2 2 3 3 5 ------ multiset 赋值操作 ------ 2 2 3 3 5 10 20 30 2 2 3 3 5
set 与 multiset 的迭代器 1 2 3 4 5 6 7 8 9 set.begin (); set.end (); set.rbegin (); set.rend (); multiset.begin (); multiset.end (); multiset.rbegin (); multiset.rend ();
set 与 multiset 的常用操作 提示
multiset 的特性及用法和 set 完全相同,唯一的区别在于它允许元素重复。因此在下述的例子中只介绍 set 的常用操作,而 multiset 的常用操作不再累述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <iostream> #include <set> using namespace std;void printSet (set<int > &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } int main () { cout << "------ set 插入操作 ------" << endl; set<int > s1; s1.insert (3 ); s1.insert (9 ); s1.insert (5 ); s1.insert (8 ); printSet (s1); cout << "------ set 大小操作 ------" << endl; cout << "size = " << s1.size () << endl; cout << "isEmpty = " << (s1.empty () ? "true" : "false" ) << endl; cout << "------ set 查找操作 ------" << endl; int count = s1.count (8 ); cout << "count = " << count << endl; set<int >::iterator pos = s1.find (8 ); if (pos != s1.end ()) { cout << "found value for " << *pos << endl; } else { cout << "not found value" << endl; } set<int >::iterator pos2 = s1.lower_bound (8 ); if (pos2 != s1.end ()) { cout << "found lower_bound(8) result is " << *pos2 << endl; } else { cout << "note found lower_bound(8) result" << endl; } set<int >::iterator pos3 = s1.upper_bound (8 ); if (pos3 != s1.end ()) { cout << "found upper_bound(8) result is " << *pos3 << endl; } else { cout << "note found upper_bound(8) result" << endl; } pair<set<int >::iterator, set<int >::iterator> result = s1.equal_range (8 ); if (result.first != s1.end ()) { cout << "found equal_range(8) first result is " << *result.first << endl; } else { cout << "note found equal_range(8) first result" << endl; } if (result.second != s1.end ()) { cout << "found equal_range(8) second result is " << *result.second << endl; } else { cout << "note found equal_range(8) second result" << endl; } cout << "------ set 删除操作 ------" << endl; s1.erase (s1.begin ()); printSet (s1); s1.erase (5 ); printSet (s1); cout << "------ set 清空操作 ------" << endl; s1.clear (); printSet (s1); return 0 ; }
程序运行输出的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ------ set 插入操作 ------ 3 5 8 9 ------ set 大小操作 ------ size = 4 isEmpty = false ------ set 查找操作 ------ count = 1 found value for 8 found lower_bound(8) result is 8 found upper_bound(8) result is 9 found equal_range(8) first result is 8 found equal_range(8) second result is 9 ------ set 删除操作 ------ 5 8 9 8 9 ------ set 清空操作 ------
set 不允许插入重复元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <set> using namespace std;void printSet (set<int > &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } int main () { set<int > s1; pair<set<int >::iterator, bool > result = s1.insert (10 ); if (result.second) { cout << "insert success" << endl; } else { cout << "insert failed" << endl; } result = s1.insert (10 ); if (result.second) { cout << "insert success" << endl; } else { cout << "insert failed" << endl; } printSet (s1); return 0 ; }
程序运行输出的结果如下:
1 2 3 insert success insert failed 10
set 对元素进行排序操作 set 容器默认会对基础数据类型的元素进行升序排序(从小到大),若需要更改 set 容器的排序规则,需要在 set 容器初始化的时候指定排序规则。值得一提的是,如果需要自定义 set 容器的排序规则,那么就需要使用到 C++ 的仿函数(Functor),也被称作函数对象或者伪函数。
1 2 set<int , less<int >> setIntA; set<int , greater<int >> setIntB;
提示
set<int>
相当于 set<int, less<int>>
,两者都是按升序方式排列元素(从小到大)。less<int>
与 greater<int>
中的 int
可以改成其它类型,该类型主要要跟 set 容纳的数据类型一致。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <iostream> #include <set> using namespace std;class myCustomCompare {public : bool operator () (const int &v1, const int &v2) { return v1 > v2; } }; void printSet (set<int > &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } void printGreaterSet (set<int , greater<int >> &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } void printCustomCompareSet (set<int , myCustomCompare> &s) { for (set<int >::iterator it = s.begin (); it != s.end (); it++) { cout << *it << " " ; } cout << endl; } int main () { set<int , less<int >> s2; s2.insert (5 ); s2.insert (1 ); s2.insert (7 ); s2.insert (3 ); s2.insert (9 ); printSet (s2); set<int , greater<int >> s3; s3.insert (5 ); s3.insert (1 ); s3.insert (7 ); s3.insert (3 ); s3.insert (9 ); printGreaterSet (s3); set<int , myCustomCompare> s4; s4.insert (5 ); s4.insert (1 ); s4.insert (7 ); s4.insert (3 ); s4.insert (9 ); s4.insert (9 ); printCustomCompareSet (s4); return 0 ; }
程序运行输出的结果如下:
1 2 3 1 3 5 7 9 9 7 5 3 1 9 7 5 3 1
set 插入自定义数据类型 特别注意
由于 set 容器中的元素默认会按一定的顺序排列,所以往 set 容器插入自定义数据类型时,必须在 set 容器初始化的时候指定仿函数(即自定义的排序规则),否则无法向 set 容器中插入自定义数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <iostream> #include <set> #include <string> using namespace std;class Person {public : Person (string name, int age) { this ->name = name; this ->age = age; } string getName () const { return this ->name; } int getAge () const { return this ->age; } private : string name; int age; }; class myCompare {public : bool operator () (const Person &p1, const Person &p2) { return p1.getAge () > p2.getAge (); } }; void printCustomSet (set<Person, myCompare> &p) { for (set<Person, myCompare>::iterator it = p.begin (); it != p.end (); it++) { cout << "name: " << it->getName () << ", age: " << it->getAge () << endl; } } int main () { set<Person, myCompare> s1; Person p1 ("Tom" , 18 ) ; Person p2 ("Peter" , 20 ) ; Person p3 ("Jim" , 15 ) ; Person p4 ("David" , 28 ) ; s1.insert (p1); s1.insert (p2); s1.insert (p3); s1.insert (p4); printCustomSet (s1); return 0 ; }
程序运行输出的结果如下:
1 2 3 4 name: David, age: 28 name: Peter, age: 20 name: Tom, age: 18 name: Jim, age: 15
map 和 multimap 容器 map 和 multimap 容器的概念 map 是 STL 的一个关联式容器,它提供一对一(其中第一个称为关键字,每个关键字只能在 map 中出现一次,第二个称为该关键字的值)的数据处理能力。map 容器存储的都是 pair
对象,也就是用 pair
类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int
、double
等)、使用结构体或类自定义的类型。通常情况下,map 容器中存储的各个键值对都选用 string 字符串作为键的类型。map 内部自建了一颗红黑树 (一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在 map 内部所有的数据都是有序的。 map 的特点是增加和删除节点对迭代器的影响很小,除了那个被操作的节点,对其他的节点都没有什么影响。使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。 map 可以根据 key 值快速查找记录,复杂度在 log(n)
级别,如果有 1000 条记录,最多查找 10 次,如果有 1000000 条记录,最多查找 20 次。**multimap 与 map 的区别:map 支持唯一键值,每个键只能出现一次;而在 multimap 中相同键可以出现多次。
总结
map 是标准的关联式容器,一个 map 是一个键值对序列,即 (key - value) 对。它提供基于 key 的快速检索能力。 map 中 key 值是唯一的。集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入到指定位置。 map 的底层实现采用红黑树变体的平衡二叉树的数据结构,在插入操作和删除操作上比 vector 快。 map 可以直接存取 key 所对应的 value,支持 []
操作符,如 map[key] = value
。 multimap 与 map 的区别:map 支持唯一键值,每个键只能出现一次;而在 multimap 中相同键可以出现多次。 map 支持 []
操作符,而 multimap 不支持 []
操作符。 map 和 multimap 容器的使用 map 和 multimap 的构造与赋值 默认构造函数 map 与 multimap 采用模板类实现,map 对象的默认构造形式:map<T1, T2> mapTT;
,其中 <>
尖括号内还可以设置指针类型或自定义类型
1 2 map<int , char > mapA; map<string, float > mapB;
1 2 multimap<int , char > multimapA; multimap<string, float > multimapB;
赋值操作说明 1 2 3 map (const map &mp); map& operator =(const map &mp); map.swap (mp);
1 2 3 multimap (const map &mp); multimap& operator =(const map &mp); multimap.swap (mp);
map 和 multimap 的常用操作 multimap 的特性及用法和 map 完全相同,唯一的区别在于它允许相同的键可以出现多次。因此在下述的例子中只介绍 map 的常用操作,而 multimap 的常用操作不再累述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 #include <iostream> #include <map> using namespace std;int main () { cout << "------ map 插入操作 ------" << endl; map<int , int > m; m.insert (pair<int , int >(1 , 2 )); m.insert (make_pair (3 , 4 )); m.insert (map<int , int >::value_type (5 , 6 )); m[7 ] = 8 ; cout << "------ map 遍历操作 ------" << endl; for (map<int , int >::iterator it = m.begin (); it != m.end (); it++) { cout << "key: " << it->first << " , value: " << it->second << endl; } for (auto it = m.begin (); it != m.end (); it++) { cout << "key = " << it->first << " , value = " << it->second << endl; } cout << "------ map 存取操作 ------" << endl; map<int , int >::iterator item = m.find (5 ); cout << "key = " << item->first << " , value = " << item->second << endl; if (m.find (100 ) == m.end ()) { cout << "key " << 100 << " not exist" << endl; } if (m.count (5 ) == 1 ) { cout << "key " << 5 << " existed" << endl; } map<int , int >::iterator result1 = m.lower_bound (3 ); if (result1 != m.end ()) { cout << "found result for lower_bound(3), key: " << result1->first << ", value: " << result1->second << endl; } else { cout << "not found result for lower_bound(3)" ; } map<int , int >::iterator result2 = m.upper_bound (3 ); if (result2 != m.end ()) { cout << "found result for upper_bound(3), key: " << result2->first << ", value: " << result2->second << endl; } else { cout << "not found result for upper_bound(3)" ; } pair<map<int , int >::iterator, map<int , int >::iterator> result3 = m.equal_range (3 ); if (result3.first != m.end ()) { cout << "found lower_bound for equal_range(3), key: " << result3.first->first << ", value: " << result3.first->second << endl; } else { cout << "not found lower_bound for equal_range(3)" << endl; } if (result3.second != m.end ()) { cout << "found upper_bound for equal_range(3), key: " << result3.second->first << ", value: " << result3.second->second << endl; } else { cout << "not found upper_bound for equal_range(3)" ; } cout << "------ map 删除操作 ------" << endl; m.erase (7 ); for (auto it = m.begin (); it != m.end (); it++) { cout << "key = " << it->first << " , value = " << it->second << endl; } }
程序运行输出的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ------ map 插入操作 ------ ------ map 遍历操作 ------ key: 1 , value: 2 key: 3 , value: 4 key: 5 , value: 6 key: 7 , value: 8 key = 1 , value = 2 key = 3 , value = 4 key = 5 , value = 6 key = 7 , value = 8 ------ map 存取操作 ------ key = 5 , value = 6 key 100 not exist key 5 existed found result for lower_bound(3), key: 3, value: 4 found result for upper_bound(3), key: 5, value: 6 found lower_bound for equal_range(3), key: 3, value: 4 found upper_bound for equal_range(3), key: 5, value: 6 ------ map 删除操作 ------ key = 1 , value = 2 key = 3 , value = 4 key = 5 , value = 6
map 对元素进行排序操作 map 容器默认会对基础数据类型的 key 进行升序排序(从小到大),若需要更改 map 容器的排序规则,需要在 map 容器初始化的时候指定排序规则。值得一提的是,如果需要自定义 map 容器的排序规则,那么就需要使用到 C++ 的仿函数(Functor),也被称作函数对象或者伪函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <map> using namespace std;class myCompare {public : bool operator () (const int &v1, const int &v2) { return v1 > v2; } }; void printMap (map<int , int , myCompare> &m) { for (map<int , int , myCompare>::iterator it = m.begin (); it != m.end (); it++) { cout << "key = " << it->first << ", value = " << it->second << endl; } } int main () { map<int , int , myCompare> m1; m1.insert (make_pair (1 , 8 )); m1.insert (make_pair (7 , 2 )); m1.insert (make_pair (4 , 10 )); m1.insert (make_pair (9 , 5 )); printMap (m1); return 0 ; }
程序运行输出的结果如下:
1 2 3 4 key = 9, value = 5 key = 7, value = 2 key = 4, value = 10 key = 1, value = 8
map 使用自定义数据类型作为 Key 特别注意
由于 map 容器中的元素默认会根据 key 按一定的顺序排列,所以往 map 容器插入 key 为自定义数据类型的元素时,必须在 map 容器初始化的时候指定仿函数(即自定义的排序规则),否则无法向 map 容器中插入 key 为自定义数据类型的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <map> using namespace std;class Person {public : Person (string name, int age) { this ->name = name; this ->age = age; } string getName () const { return this ->name; } int getAge () const { return this ->age; } private : string name; int age; }; class myCompare {public : bool operator () (const Person &p1, const Person &p2) { return p1.getAge () > p2.getAge (); } }; void printMap (map<Person, string, myCompare> &m) { for (map<Person, string, myCompare>::iterator it = m.begin (); it != m.end (); it++) { cout << "age = " << it->first.getAge () << ", name = " << it->first.getName () << ", role = " << it->second << endl; } } int main () { map<Person, string, myCompare> m1; m1.insert (make_pair (Person ("Tom" , 19 ), "Student" )); m1.insert (make_pair (Person ("Jim" , 18 ), "Student" )); m1.insert (make_pair (Person ("Peter" , 28 ), "Teacher" )); printMap (m1); return 0 ; }
程序运行输出的结果如下:
1 2 3 age = 28 , name = Peter, role = Teacher age = 19 , name = Tom, role = Student age = 18 , name = Jim, role = Student