STL map和unordered_map

2023-03-27 22:50:25

1.       map概念

STL中的map是一种关联容器,用于存储键值对,可以根据键快速查找对应的值。简单用法描述如下:

 

声明一个map对象:std::map<key_type, value_type> map_name; 。

插入元素:map_name.insert(std::make_pair(key, value)); 。

查找元素:map_name.find(key) 。

删除元素:map_name.erase(key) 。

遍历元素:可以使用迭代器,在循环中依次访问所有元素。

值得注意的是,map是按照键值进行排序的,因此可以使用lower_bound、upper_bound等函数对其进行查找和遍历。另外,map还提供了多种成员函数,例如empty、size、clear等等,可以方便地对其进行操作和管理。

 

STL的map内部采用红黑树来实现,因此查找、插入、删除等操作的时间复杂度都是O(logn),非常高效。

 

2.       成员函数:

STL的map类提供了丰富的成员函数,以下是其中的一些:

 

构造函数和析构函数:

map():默认构造函数。

map(const map &other):拷贝构造函数。

~map():析构函数。

 

迭代器相关:

begin():返回指向map容器中第一个元素的迭代器。

end():返回指向map容器中最后一个元素后面的迭代器。

rbegin():返回指向map容器中最后一个元素的逆向迭代器。

rend():返回指向map容器中第一个元素前面的逆向迭代器。

 

访问元素:

operator[]:通过键访问对应的值。

at():通过键访问对应的值,如果键不存在则抛出异常。

front():返回map容器中第一个元素的引用。

back():返回map容器中最后一个元素的引用。

 

修改容器:

insert():插入一个新的元素。

erase():删除某个元素。

clear():清空map容器中的所有元素。

 

容器状态:

size():返回map容器中的元素个数。

empty():判断map容器是否为空。

 

查找元素:

find():查找某个键对应的迭代器,如果不存在则返回end()。

count():返回某个键在map容器中出现的次数,因为map中每个键只能出现一次,所以返回值只能是0或1。

lower_bound():返回一个指向第一个等于或大于某个键的迭代器。

upper_bound():返回一个指向第一个大于某个键的迭代器。

equal_range():返回一个迭代器对,其中第一个迭代器指向第一个等于或大于某个键的元素,第二个迭代器指向第一个大于该键的元素。

以上只是map类的一部分成员函数,除此之外,还有很多其他的成员函数,如关于分配器的成员函数等等。

 

3.       map典型用法

map的典型用法包括:

 

单词统计:将文本中的单词作为键,出现次数作为值,插入到map中,最后遍历map输出结果。

 

学生成绩管理:将学生的姓名作为键,成绩作为值,插入到map中,可以根据姓名查找成绩,也可以按照成绩排序输出名字。

 

缓存设计:利用map来实现缓存,将数据的键作为map的键,数据本身作为map的值,可以快速查找缓存中是否存在某个数据。

 

路由表实现:将IP地址作为键,路由信息作为值,插入到map中,可以快速查找某个IP对应的路由信息。

 

订单管理:将订单号作为键,订单信息作为值,插入到map中,可以根据订单号查找订单信息,也可以按照下单时间排序输出订单列表。

 

总之,map是一种非常实用的容器,可以用于各种各样的场景,尤其是需要快速查找数据的场景。

 

4.       map代码示例:

 1 #include <iostream>
 2 
 3 #include <map>
 4 
 5 #include <string>
 6 
 7  
 8 
 9 using namespace std;
10 
11  
12 
13 int main() {
14 
15     // 定义一个map,键为string类型,值为int类型
16 
17     map<string, int> word_count;
18 
19     string word;
20 
21  
22 
23     // 读取输入的单词并计数
24 
25     while (cin >> word) {
26 
27         ++word_count[word];
28 
29     }
30 
31  
32 
33     // 输出结果
34 
35     for (const auto& kv : word_count) {
36 
37         cout << kv.first << " : " << kv.second << endl;
38 
39     }
40 
41  
42 
43     return 0;
44 
45 }

 

 

这段代码使用了STL的map容器来计算输入中每个单词出现的次数,并输出结果。其中,map的键为string类型,值为int类型,使用了auto关键字来遍历map中的键值对。这个示例代码演示了map的用法,包括定义、插入、遍历和访问元素等。

 

再来一个基本用法的

 1 #include <iostream>
 2 #include <map>
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 int main() {
 8     // 创建一个空的map
 9     map<string, int> myMap;
10 
11     // 插入元素
12     myMap.insert(pair<string, int>("Alice", 20));
13     myMap.insert(pair<string, int>("Bob", 25));
14     myMap.insert(pair<string, int>("Charlie", 30));
15 
16     // 访问元素
17     cout << "Alice's age is " << myMap["Alice"] << endl;
18 
19     // 遍历map
20     for (auto it = myMap.begin(); it != myMap.end(); it++) {
21         cout << it->first << " is " << it->second << " years old." << endl;
22     }
23 
24     // 删除元素
25     myMap.erase("Charlie");
26 
27     // 判断元素是否存在
28     if (myMap.count("Charlie") == 0) {
29         cout << "Charlie is not in the map." << endl;
30     }
31 
32     // 清空map
33     myMap.clear();
34 
35     return 0;
36 }

 

 

5.       map和unordered_map的区别

map和unordered_map都是STL中的关联容器,用于存储键值对。

它们的主要区别在于底层实现的数据结构和特点。

 

底层数据结构

map底层采用红黑树实现,因此它的元素是有序的,可以进行快速查找、删除和插入操作,时间复杂度为O(log n)。

而unordered_map底层采用哈希表实现,因此它的元素是无序的,可以进行快速查找、删除和插入操作,时间复杂度为O(1)。但是,哈希表需要占用较大的内存空间来存储哈希桶,而红黑树则不需要。

 

特点

由于map是有序的,因此它支持按照键的大小进行遍历和查找操作,也可以进行区间查找和删除操作。

而unordered_map则不支持这些操作,只能进行查找、插入和删除操作。

 

另外,由于哈希表需要使用哈希函数来计算键的哈希值,因此unordered_map需要保证键类型具有可哈希性。如果键类型没有定义哈希函数,则需要自己定义一个哈希函数,并将其传递给unordered_map。

 

综上所述,map和unordered_map都有各自的优点和缺点,可以根据实际需求选择使用。如果需要支持有序遍历和区间操作,可以选择map;如果需要快速查找、插入和删除,可以选择unordered_map。

 

 

6.       unordered_map成员函数

unordered_map是STL中的关联容器,用于存储键值对。它提供了以下常用的成员函数:

 

插入元素

insert():插入单个元素或多个元素到unordered_map中。插入单个元素时,可以使用make_pair()函数创建一个键值对,也可以使用pair<>类型的变量。插入多个元素时,需要传递一个迭代器区间,例如:

unordered_map<int, string> m;

m.insert(make_pair(1, "apple"));

m.insert(pair<int, string>(2, "banana"));

m.insert({3, "orange"});

unordered_map<int, string> m2 = {{4, "pear"}, {5, "kiwi"}};

m.insert(m2.begin(), m2.end());

 

访问元素

operator[]:通过键访问元素的值,如果键不存在,则会插入一个默认构造的元素。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}};

cout << m[1] << endl;  // 输出 apple

cout << m[3] << endl;  // 输出空字符串,并插入一个默认构造的元素

at():通过键访问元素的值,如果键不存在,则会抛出out_of_range异常。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}};

cout << m.at(1) << endl;  // 输出 apple

cout << m.at(3) << endl;  // 抛出out_of_range异常

 

删除元素

erase():删除单个元素或多个元素。删除单个元素时,需要传递键值;删除多个元素时,需要传递一个迭代器区间。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}, {3, "orange"}};

m.erase(2);  // 删除键为2的元素

m.erase(m.begin(), m.find(3));  // 删除键小于3的元素

clear():删除所有元素。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}};

m.clear();

 

查找元素

find():通过键查找元素,返回指向该元素的迭代器;如果键不存在,则返回unordered_map::end()迭代器。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}};

auto it = m.find(2);

if (it != m.end()) {

    cout << "Found " << it->second << endl;

} else {

    cout << "Not found" << endl;

}

count():统计键在unordered_map中出现的次数,返回0或1。例如:

unordered_map<int, string> m = {{1, "apple"}, {2, "banana"}};

if (m.count(2) == 1) {

    cout << "Found" << endl;

} else {

    cout << "Not found" << endl;

}

其他

size():返回unordered_map中元素的个数。

empty():判断unordered_map是否为空。

bucket_count():返回unordered_map底层哈希表的桶数。

load_factor():返回unordered_map底层哈希表的负载因子。

rehash():重新分配unordered_map底层哈希表的桶数。

  

7.       unordered_map的哈希函数用法

在使用unordered_map时,需要为键类型定义一个哈希函数,以便unordered_map能够根据键值快速地定位元素。

哈希函数的定义方法有多种,这里介绍两种常用的方法。

 

自定义哈希函数

可以自己定义一个哈希函数,用于计算键的哈希值。哈希函数的定义需要满足以下要求:

 

返回值类型必须为size_t,表示哈希值的类型。

参数类型必须为键类型。

函数体中需要使用哈希算法计算键的哈希值,并返回结果。

例如,下面是一个自定义的哈希函数,用于计算string类型的哈希值:

 1 size_t my_hash(const string& s) {
 2 
 3     size_t h = 0;
 4 
 5     for (const char& c : s) {
 6 
 7         h = 31 * h + static_cast<size_t>(c);
 8 
 9     }
10 
11     return h;
12 
13 }

 

然后,可以将这个哈希函数作为模板参数传递给unordered_map:

unordered_map<string, int, decltype(&my_hash)> m(10, &my_hash);

 

使用标准库提供的哈希函数

C++标准库提供了一些通用的哈希函数,例如std::hash,可以用于计算基本数据类型和STL容器类型的哈希值。使用标准库提供的哈希函数,需要满足以下要求:

 

在std命名空间中,因此需要使用std::hash。

必须实现std::hash_traits,以便unordered_map能够识别该哈希函数。

例如,下面是使用std::hash计算string类型的哈希值的示例:

 1 #include <iostream>
 2 
 3 #include <unordered_map>
 4 
 5 #include <string>
 6 
 7  
 8 
 9 // 实现std::hash_traits
10 
11 namespace std {
12 
13     template <>
14 
15     struct hash<string> {
16 
17         size_t operator()(const string& s) const {
18 
19             return hash<const char*>()(s.c_str());
20 
21         }
22 
23     };
24 
25 }
26 
27  
28 
29 int main() {
30 
31     unordered_map<string, int> m = {{"apple", 1}, {"banana", 2}};
32 
33     cout << m["apple"] << endl;  // 输出 1
34 
35     return 0;
36 
37 }

 

在这个示例中,我们重载了std::hash模板,实现了计算string类型哈希值的函数。然后,就可以直接使用unordered_map<string, int>了。

 

8.       unordered_map典型用法

unordered_map是STL中的关联容器,用于存储键值对。它的主要优点是快速查找、插入和删除元素,时间复杂度为O(1)。因此,unordered_map适用于以下场景:

 

需要快速查找元素

unordered_map采用哈希表实现,可以在常数时间内查找元素。因此,如果需要快速查找某个元素是否存在,可以使用unordered_map。

 

需要频繁插入和删除元素

由于unordered_map采用哈希表实现,可以在常数时间内插入和删除元素。因此,如果需要频繁地插入和删除元素,可以使用unordered_map。

 

不需要按照键排序

unordered_map的元素是无序的,因此不能按照键进行排序。如果需要按照键排序,则需要使用map。

 

键具有可哈希性

由于unordered_map采用哈希表实现,需要使用哈希函数计算键的哈希值。因此,如果键类型不具有可哈希性,则不能使用unordered_map。

 

综上所述,如果需要快速查找、插入和删除元素,并且键具有可哈希性,可以使用unordered_map。如果需要按照键排序或者键类型不具有可哈希性,则需要使用其他容器,例如map或vector。