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。