1.Map
map是一个有序的键值对容器,其中的键是唯一的,不允许重复。
它基于红黑树实现,因此插入、删除和查找操作都具有较高的效率。
元素会按照键的升序排序,默认情况下,也可以自定义排序规则。
std::map提供的一些主要操作:
插入元素:
insert():插入一个键值对或一个范围的键值对到map中。
emplace():在map中构造一个键值对。
operator[]:访问指定键对应的值,若键不存在则插入默认值。
删除元素:
erase():根据键删除一个或多个元素。
clear():清空map中的所有元素。
访问元素:
at():根据键访问对应的值,若键不存在则抛出异常。
find():根据键查找对应的元素,返回迭代器。
count():根据键统计在map中出现的次数。
大小和容量:
size():返回map中键值对的个数。
empty():检查map是否为空。
遍历元素:
使用迭代器遍历map中的键值对。
用一段代码演示Map的主要功能:
#include <iostream>
#include <map>
#include <string>
int main() {
// 创建一个map容器,键为int类型,值为string类型
std::map<int, std::string> myMap;
// 三种插入元素的方式
myMap.insert({ 3, "Three" });//insert接受的是键-值对,用花括号可以插入键-值对,或者用std::make_pair 函数来创建一个键值对,然后将其作为参数传递给 insert 函数。例如:
myMap.insert(std::make_pair(1, "One"));
myMap[2] = "Two";
//在你输入之后,map会自动按照键值1 2 3进行升序排列,所以最后循环打印时,先输出键值2的键值对,后输出键值为3的键值对
// 查找元素
std::map<int, std::string>::iterator it = myMap.find(2);//寻找键值为2的元素,如果没有,it得到的值是myMap.end()
if (it != myMap.end()) {
std::cout << "Found element with key 2: " << it->second;//与(*it).second等效 << std::endl;
}
else {
std::cout << "Element with key 2 not found" << std::endl;
}
// 删除元素
myMap.erase(1);
//如果 myMap 中存在键为 1 的元素,则该元素将被从容器中删除;如果不存在,则不进行任何操作。
//注意,如果 erase 函数成功删除了元素,则返回值为 1,表示删除了一个元素;如果指定的键不存在,返回值为 0,表示没有删除任何元素。
// 遍历输出元素
std::cout << "Map elements in ascending order: " << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
//也可以写for (it = myMap.begin(); it != myMap.end(); it++) {std::cout << (*it).first << ": " << (*it).second << std::endl;}
return 0;
}
count功能:
#include <iostream>
#include <map>
int main() {
std::map<int, int> myMap;
// 添加一些键值对到map中
myMap[1] = 10;
myMap[2] = 20;
myMap[3] = 30;
myMap[4] = 10;
myMap[5] = 10;
// 统计键为1的出现次数
int count = myMap.count(1);
std::cout << "键为1的出现次数:" << count << std::endl;
// 统计键为10的出现次数
count = myMap.count(10);
std::cout << "键为10的出现次数:" << count << std::endl;
// 统计键为6的出现次数
count = myMap.count(6);
std::cout << "键为6的出现次数:" << count << std::endl;
return 0;
}
输出结果将会是:
键为1的出现次数:1
键为10的出现次数:0
键为6的出现次数:0
operator[]和at():
`operator[]`和`at()`都可以用于访问map中的元素,但它们有一些区别:
1. 行为不同:`operator[]`在访问键不存在时会自动插入一个具有默认值的键值对,而`at()`在访问不存在的键时会抛出`std::out_of_range`异常。
2. 返回值不同:`operator[]`返回键对应的值的引用,如果键不存在,则返回一个插入的默认值的引用;`at()`返回键对应的值的拷贝,并且如果键不存在,则抛出异常。
在std::map中,at()和operator[]可以用于访问元素,但它们不属于索引和随机访问。这两个方法都是通过键来访问元素,而不是通过索引或随机访问。它们基于红黑树的特性,在平均情况下具有对数时间复杂度(O(log n))。
`std::map`的访问方式和索引/随机访问之间有以下区别:
1. 数据结构:`std::map`是基于红黑树实现的关联容器,而索引/随机访问通常是针对支持连续内存存储的容器(如`std::vector`、`std::array`等)。
2. 访问方式:`std::map`通过键来访问元素,而索引/随机访问是通过元素在容器中的位置(索引)或迭代器来访问元素。
3. 时间复杂度:`std::map`的查找、插入和删除操作都具有对数时间复杂度(O(log n)),而索引/随机访问的时间复杂度通常是常量时间复杂度(O(1))。
以下是operator[]和at()的示例代码
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> myMap;
// 使用operator[]访问键对应的值,如果键不存在则插入默认值
myMap["apple"] = 10;
myMap["banana"] = 20;
myMap["orange"] = 30;
// 访问已存在的键
std::cout << "键为apple的值:" << myMap["apple"] << std::endl;
// 访问不存在的键,插入默认值
std::cout << "键为grape的值:" << myMap["grape"] << std::endl;
// 输出map中的所有键值对
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
输出结果将会是:
键为apple的值:10
键为grape的值:0
apple: 10
banana: 20
grape: 0
orange: 30
我们使用`operator[]`访问不存在的键`"grape"`,由于该键不存在于map中,会插入默认值0,并输出结果。在最后的循环打印中,因为使用的是值引用,而不是迭代器,所以输出不用(*it).first解引用或者it->first,而是直接使用pair.first。
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> myMap;
// 添加一些键值对到map中
myMap["apple"] = 10;
myMap["banana"] = 20;
myMap["orange"] = 30;
// 使用at函数获取键对应的值
int value = myMap.at("apple");
std::cout << "键为apple的值:" << value << std::endl;
// 尝试获取不存在的键的值
try {
value = myMap.at("grape");
std::cout << "键为grape的值:" << value << std::endl;
}
catch (std::out_of_range) {
std::cout << "键为grape的值不存在。" << std::endl;
}
return 0;
}
输出结果将会是:
键为apple的值:10
键为grape的值不存在。
2.try-catch
try-catch是一种异常处理机制,try中是可能引发异常的代码,catch后的()是可能出现的异常,{}是处理异常的方法
如果不加try-catch,上面的程序会弹出异常:
注意,这是使用at获取键值时抛出的异常。如果使用迭代器获取,不会抛出异常,而是直接返回end()。在第一个例子中,程序使用if-else解决了这个问题。
try-catch的基本用法如下:
try {
// 可能引发异常的代码
} catch (ExceptionType1 e1) {
// 处理ExceptionType1类型的异常
} catch (ExceptionType2 e2) {
// 处理ExceptionType2类型的异常
} catch (...) {
// 处理其他类型的异常
}
可以添加最后一个catch块使用省略号(...)来捕捉其他未被前面的catch块捕捉到的异常。
try-catch还可以嵌套使用,以便更细致地处理不同类型的异常。在嵌套的try-catch块中,内层catch块可以捕获并处理内层try块中发生的异常,如果内层catch块没有捕获到异常,异常将会被传递到外层的catch块。
3.unordered_map
unordered_map和map是C++中两种不同的容器类,它们在实现和使用上有一些区别。
1. 底层实现方式不同:unordered_map使用哈希表来存储键值对,而map使用红黑树来存储键值对。哈希表的插入、查找和删除操作的平均时间复杂度为O(1),而红黑树的平均时间复杂度为O(log n)。
2. 元素的顺序不同:unordered_map中的元素是无序的,插入的顺序不会影响元素的存储顺序。而map中的元素是按照键的大小进行排序的,插入的顺序会影响元素的存储顺序。
3. 查找效率不同:由于unordered_map使用哈希表实现,查找特定元素的效率比map高。在unordered_map中,使用键进行查找的平均时间复杂度为O(1),而在map中,使用键进行查找的平均时间复杂度为O(log n)。
下面用一段代码展示under_order的主要功能
#include <iostream>
#include <unordered_map>
int main() {
// 创建一个unordered_map
std::unordered_map<std::string, int> umap;
// 向unordered_map中插入键值对
umap.insert(std::make_pair("apple", 10));
umap.insert(std::make_pair("banana", 5));
umap.insert(std::make_pair("orange", 8));
// 使用下标运算符访问元素
std::cout << "The price of apple is: " << umap["apple"] << std::endl;
// 使用迭代器遍历unordered_map
std::cout << "All the fruits and their prices: " << std::endl;
for (auto it = umap.begin(); it != umap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
// 查找元素
std::string fruit = "banana";
auto it = umap.find(fruit);
if (it != umap.end()) {
std::cout << "The price of " << fruit << " is: " << it->second << std::endl;
} else {
std::cout << fruit << " is not found in the unordered_map." << std::endl;
}
// 删除元素
umap.erase("orange");
// 判断unordered_map是否为空
if (umap.empty()) {
std::cout << "The unordered_map is empty." << std::endl;
} else {
std::cout << "The unordered_map is not empty." << std::endl;
}
// 获取unordered_map的大小
std::cout << "The size of unordered_map is: " << umap.size() << std::endl;
return 0;
}
这段代码演示了unordered_map的基本用法。首先创建一个空的unordered_map,然后使用insert函数插入键值对。可以使用下标运算符或者迭代器来访问unordered_map中的元素。使用find函数可以查找特定的元素,如果找到则返回对应的迭代器,否则返回unordered_map的end迭代器。使用erase函数可以删除指定的元素。使用empty函数判断unordered_map是否为空,使用size函数获取unordered_map的大小。