diff --git a/STL/STL-Set/README.MD b/STL/STL-Set/README.MD index e413887..da5e2e9 100644 --- a/STL/STL-Set/README.MD +++ b/STL/STL-Set/README.MD @@ -204,3 +204,301 @@ if (auto [it, ok] = s.insert(10); ok) { * 🧩 `unordered_set` 的比较与选择策略 是否要我继续讲 `multiset` 和 `unordered_set`? + + +
+ {<'a', 1>, <'b', 2>, <'c', 3>}
+ {<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}
++值得一提的是,set 容器定义于+ 对于初学者来说,切勿尝试直接修改 set 容器中已存储元素的值,这很有可能破坏 set 容器中元素的有序性,最正确的修改 set 容器中元素值的做法是:先删除该元素,然后再添加一个修改后的元素。
+
<set>
头文件,并位于 std 命名空间中。因此如果想在程序中使用 set 容器,该程序代码应先包含如下语句:
++#include <set> +using namespace std;+注意,第二行代码不是必需的,如果不用,则后续程序中在使用 set 容器时,需手动注明 std 命名空间(强烈建议初学者使用)。
+template < class T, // 键 key 和值 value 的类型 + class Compare = less<T>, // 指定 set 容器内部的排序规则 + class Alloc = allocator<T> // 指定分配器对象的类型 + > class set;+注意,由于 set 容器存储的各个键值对,其键和值完全相同,也就意味着它们的类型相同,因此 set 容器类模板的定义中,仅有第 1 个参数用于设定存储数据的类型。
+++ 对于 set 类模板中的 3 个参数,后 2 个参数自带默认值,且几乎所有场景中只需使用前 2 个参数,第 3 个参数不会用到。
+
+std::set<std::string> myset;+
++由此就创建好了一个 set 容器,该容器采用默认的+ 如果程序中已经默认指定了 std 命令空间,这里可以省略 std::。
+
std::less<T>
规则,会对存储的 string 类型元素做升序排序。注意,由于 set 容器支持随时向内部添加新的元素,因此创建空 set 容器的方法是经常使用的。+std::set<std::string> myset{"http://c.biancheng.net/java/", + "http://c.biancheng.net/stl/", + "http://c.biancheng.net/python/"};+由此即创建好了包含 3 个 string 元素的 myset 容器。由于其采用默认的 std::less<T> 规则,因此其内部存储 string 元素的顺序如下所示: +
+ "http://c.biancheng.net/java/"
+ "http://c.biancheng.net/python/"
+ "http://c.biancheng.net/stl/"
+std::set<std::string> copyset(myset); +//等同于 +//std::set<std::string> copyset = myset+该行代码在创建 copyset 容器的基础上,还会将 myset 容器中存储的所有元素,全部复制给 copyset 容器一份。
+set<string> retSet() { + std::set<std::string> myset{ "http://c.biancheng.net/java/", + "http://c.biancheng.net/stl/", + "http://c.biancheng.net/python/" }; + return myset; +} +std::set<std::string> copyset(retSet()); +//或者 +//std::set<std::string> copyset = retSet();+
+ 注意,由于 retSet() 函数的返回值是一个临时 set 容器,因此在初始化 copyset 容器时,其内部调用的是 set 类模板中的移动构造函数,而非拷贝构造函数。
++++ 显然,无论是调用复制构造函数还是调用拷贝构造函数,都必须保证这 2 个容器的类型完全一致。
+
+std::set<std::string> myset{ "http://c.biancheng.net/java/", + "http://c.biancheng.net/stl/", + "http://c.biancheng.net/python/" }; +std::set<std::string> copyset(++myset.begin(), myset.end());+由此初始化的 copyset 容器,其内部仅存有如下 2 个 string 字符串: +
+ "http://c.biancheng.net/python/"
+ "http://c.biancheng.net/stl/"
std::less<T>
规则。其实,借助 set 类模板定义中第 2 个参数,我们完全可以手动修改 set 容器中的排序规则。比如:
++std::set<std::string,std::greater<string> > myset{ + "http://c.biancheng.net/java/", + "http://c.biancheng.net/stl/", + "http://c.biancheng.net/python/"};+通过选用 std::greater<string> 降序规则,myset 容器中元素的存储顺序为: +
+ "http://c.biancheng.net/stl/"
+ "http://c.biancheng.net/python/"
+ "http://c.biancheng.net/java/"
+ 成员方法 | ++ 功能 | +
---|---|
+ begin() | ++ 返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ end() | ++ 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ rbegin() | ++ 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 | +
+ rend() | ++ 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 | +
+ cbegin() | ++ 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 | +
+ cend() | ++ 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 | +
+ crbegin() | ++ 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 | +
+ crend() | ++ 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 | +
+ find(val) | ++ 在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ lower_bound(val) | ++ 返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ upper_bound(val) | ++ 返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ equal_range(val) | ++ 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。 | +
+ empty() | ++ 若容器为空,则返回 true;否则 false。 | +
+ size() | ++ 返回当前 set 容器中存有元素的个数。 | +
+ max_size() | ++ 返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。 | +
+ insert() | ++ 向 set 容器中插入元素。 | +
+ erase() | ++ 删除 set 容器中存储的元素。 | +
+ swap() | ++ 交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。 | +
+ clear() | ++ 清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。 | +
+ emplace() | ++ 在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。 | +
+ emplace_hint() | ++ 在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。 | +
+ count(val) | ++ 在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1。 | +
+#include <iostream> +#include <set> +#include <string> +using namespace std; + +int main() +{ + //创建空set容器 + std::set<std::string> myset; + //空set容器不存储任何元素 + cout << "1、myset size = " << myset.size() << endl; + //向myset容器中插入新元素 + myset.insert("http://c.biancheng.net/java/"); + myset.insert("http://c.biancheng.net/stl/"); + myset.insert("http://c.biancheng.net/python/"); + cout << "2、myset size = " << myset.size() << endl; + //利用双向迭代器,遍历myset + for (auto iter = myset.begin(); iter != myset.end(); ++iter) { + cout << *iter << endl; + } + return 0; +}+程序执行结果为: +
+ 1、myset size = 0
+ 2、myset size = 3
+ http://c.biancheng.net/java/
+ http://c.biancheng.net/python/
+ http://c.biancheng.net/stl/
+++ 有关表 1 中其它成员方法的用法,后续章节会做详细讲解。
+
++与此同时,在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用+ 通常情况下,map 容器中存储的各个键值对都选用 string 字符串作为键的类型。
+
std::less<T>
排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用 STL 标准库中提供的其它排序规则(比如std::greater<T>
),也可以自定义排序规则。++另外需要注意的是,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。换句话说,map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改。 ++ 关于如何自定义 map 容器的排序规则,后续章节会做详细讲解。
+
++map 容器定义在 <map> 头文件中,并位于 std 命名空间中。因此,如果想使用 map 容器,代码中应包含如下语句: ++ 前面提到,map 容器存储的都是 pair 类型的键值对元素,更确切的说,该容器存储的都是 pair<const K, T> 类型(其中 K 和 T 分别表示键和值的数据类型)的键值对元素。
+
+#include <map> +using namespace std;+
++map 容器的模板定义如下: ++ 注意,第二行代码不是必需的,如果不用,则后续程序中在使用 map 容器时,需手动注明 std 命名空间(强烈建议初学者使用)。
+
+template < class Key, // 指定键(key)的类型 + class T, // 指定值(value)的类型 + class Compare = less<Key>, // 指定排序规则 + class Alloc = allocator<pair<const Key,T> > // 指定分配器对象的类型 + > class map;+可以看到,map 容器模板有 4 个参数,其中后 2 个参数都设有默认值。大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。
+std::map<std::string, int>myMap;+
++通过此方式创建出的 myMap 容器,初始状态下是空的,即没有存储任何键值对。鉴于空 map 容器可以根据需要随时添加新的键值对,因此创建空 map 容器是比较常用的。+ 如果程序中已经默认指定了 std 命令空间,这里可以省略
+std::
。
+std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} };+由此,myMap 容器在初始状态下,就包含有 2 个键值对。
+std::map<std::string, int>myMap{std::make_pair("C语言教程",10),std::make_pair("STL教程",20)};+
+std::map<std::string, int>newMap(myMap);+由此,通过调用 map 容器的拷贝(复制)构造函数,即可成功创建一个和 myMap 完全一样的 newMap 容器。
+#创建一个会返回临时 map 对象的函数 +std::map<std::string,int> disMap() { + std::map<std::string, int>tempMap{ {"C语言教程",10},{"STL教程",20} }; + return tempMap; +} +//调用 map 类模板的移动构造函数创建 newMap 容器 +std::map<std::string, int>newMap(disMap());+
+++ 注意,无论是调用复制构造函数还是调用拷贝构造函数,都必须保证这 2 个容器的类型完全一致。
+
+std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} }; +std::map<std::string, int>newMap(++myMap.begin(), myMap.end());+这里,通过调用 map 容器的双向迭代器,实现了在创建 newMap 容器的同时,将其初始化为包含一个 {"STL教程",20} 键值对的容器。
+++ 有关 map 容器迭代器,后续章节会做详细讲解。
+
+std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} }; +std::map<std::string, int, std::less<std::string> >myMap{ {"C语言教程",10},{"STL教程",20} };+以上 2 中创建方式生成的 myMap 容器,其内部键值对排列的顺序为: +
+ <"C语言教程", 10>
+ <"STL教程", 20>
+std::map<std::string, int, std::greater<std::string> >myMap{ {"C语言教程",10},{"STL教程",20} };+此时,myMap 容器内部键值对排列的顺序为: +
+ <"STL教程", 20>
+ <"C语言教程", 10>
+++ 在某些特定场景中,我们还需要为 map 容器自定义排序规则,此部分知识后续将利用整整一节做重点讲解。
+
+ 成员方法 | ++ 功能 | +
---|---|
+ begin() | ++ 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ end() | ++ 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ rbegin() | ++ 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 | +
+ rend() | ++ 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 | +
+ cbegin() | ++ 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 | +
+ cend() | ++ 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 | +
+ crbegin() | ++ 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 | +
+ crend() | ++ 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 | +
+ find(key) | ++ 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ lower_bound(key) | ++ 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ upper_bound(key) | ++ 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 | +
+ equal_range(key) | ++ 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 | +
+ empty() | ++ 若容器为空,则返回 true;否则 false。 | +
+ size() | ++ 返回当前 map 容器中存有键值对的个数。 | +
+ max_size() | ++ 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 | +
+ operator[] | ++ map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。 | +
+ at(key) | ++ 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。 | +
+ insert() | ++ 向 map 容器中插入键值对。 | +
+ erase() | ++ 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。 | +
+ swap() | ++ 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 | +
+ clear() | ++ 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。 | +
+ emplace() | ++ 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 | +
+ emplace_hint() | ++ 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 | +
+ count(key) | ++ 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 | +
+#include <iostream> +#include <map> // map +#include <string> // string +using namespace std; + +int main() { + //创建空 map 容器,默认根据个键值对中键的值,对键值对做降序排序 + std::map<std::string, std::string, std::greater<std::string>>myMap; + //调用 emplace() 方法,直接向 myMap 容器中指定位置构造新键值对 + myMap.emplace("C语言教程","http://c.biancheng.net/c/"); + myMap.emplace("Python教程", "http://c.biancheng.net/python/"); + myMap.emplace("STL教程", "http://c.biancheng.net/stl/"); + //输出当前 myMap 容器存储键值对的个数 + cout << "myMap size==" << myMap.size() << endl; + //判断当前 myMap 容器是否为空 + if (!myMap.empty()) { + //借助 myMap 容器迭代器,将该容器的键值对逐个输出 + for (auto i = myMap.begin(); i != myMap.end(); ++i) { + cout << i->first << " " << i->second << endl; + } + } + return 0; +}+程序执行结果为: +
+ myMap size==3
+ STL教程 http://c.biancheng.net/stl/
+ Python教程 http://c.biancheng.net/python/
+ C语言教程 http://c.biancheng.net/c/
+++ 有关表 1 中其它成员函数的用法,后续章节会做详细详解。
+