关联式容器——map与set
map与set
map与set的使用
序列式容器与关联式容器概念
在 C++ 的标准模板库 (STL) 中,容器分为两大类:序列式容器和关联式容器。这两类容器各自有不同的特点和用途。
序列式容器 (Sequence Containers)
序列式容器是按顺序存储元素的容器。它们通常保留元素的插入顺序,并且支持通过下标直接访问元素。序列式容器的主要特点包括:
- 元素顺序:元素按照插入的顺序排列,可以通过索引访问。
- 动态大小:可以根据需要动态调整大小。
- 效率:在容器的末尾插入或删除元素通常是高效的。
常见的序列式容器:
vector:
动态数组,支持随机访问。
可以高效地在末尾插入和删除元素。
在中间插入和删除元素时效率较低,因为需要移动元素。
deque(双端队列):
允许在两端快速插入和删除元素。
支持随机访问,但比 vector 稍慢。
list(双向链表):
允许在任意位置快速插入和删除元素。
不支持随机访问,但支持快速前后遍历。
forward_list(单向链表):
只支持前向遍历,内存占用较少。
提供快速的插入和删除。
array:
固定大小的数组,支持快速随机访问。
不支持动态调整大小。
关联式容器 (Associative Containers)
关联式容器使用特定的规则(如键值对)来存储元素,通常不保留插入顺序。它们主要用于通过键快速查找值。关联式容器的主要特点包括:
- 基于键值对:每个元素由键和值组成,通常通过键进行访问。
- 自动排序:元素根据键进行排序,允许快速查找。
- 效率:基于平衡树或哈希表实现,支持高效的查找、插入和删除。
常见的关联式容器:
set:
存储唯一元素,并根据元素的值进行自动排序。
不允许重复元素,支持高效查找。
multiset:
类似于 set,但允许重复元素。
也会自动排序。
map:
存储键值对,每个键是唯一的,值可以重复。
根据键自动排序,支持通过键快速查找值。
multimap:
类似于 map,但允许相同的键对应多个值。
总结
序列式容器:用于顺序存储和操作元素,适合需要保持插入顺序或随机访问的场景。
关联式容器:用于高效查找、插入和删除操作,适合需要基于键进行访问的场景。
set的定义与使用
set类的介绍
如图为官网的英文介绍:
set的定义语法:
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
-
T就是set底层关键字的类型
-
set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模版参数
-
set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参数。
-
⼀般情况下,我们都不需要传后两个模版参数。
-
set底层是⽤红⿊树实现,增删查效率是 ,迭代器遍历是⾛的搜索树的中序,所以是有序的。
set的构造和迭代器
set的支持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序;⽀持迭代器就意味着⽀持范围for,set的iterator和const_iterator都不⽀持迭代器修改数据,修改关键字数据,破坏了底层搜索树的结构。
ps:不允许修改数据
set的构造函数:
//empty (1)
explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
explicit set (const allocator_type& alloc);
//range (2)
template <class InputIterator>
set (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());
//copy (3)
set (const set& x);
set (const set& x, const allocator_type& alloc);
//move (4)
set (set&& x);
set (set&& x, const allocator_type& alloc);
//initializer list (5)
set (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
- 默认构造函数
创建一个空的 set:
#include <set>
std::set<int> mySet; // 创建一个空的 set
- 使用初始列表
使用初始化列表直接创建 set:
#include <iostream>
#include <set>
std::set<int> mySet = {5, 3, 8, 1}; // 创建并初始化
for (const auto& elem : mySet) {
std::cout << elem << " "; // 输出:1 3 5 8
}
- 使用范围构造函数
从另一个容器(如 vector)复制元素:
#include <iostream>
#include <set>
#include <vector>
std::vector<int> vec = {4, 2, 7, 1};
std::set<int> mySet(vec.begin(), vec.end()); // 从 vector 初始化
for (const auto& elem : mySet) {
std::cout << elem << " "; // 输出:1 2 4 7
}
- 使用比较函数的构造函数
自定义比较规则(降序排列):
#include <iostream>
#include <set>
struct Descending {
bool operator()(int a, int b) const {
return a > b;
}
};
std::set<int, Descending> mySet = {5, 3, 8, 1}; // 使用自定义比较
for (const auto& elem : mySet) {
std::cout << elem << " "; // 输出:8 5 3 1
}
- 复制构造函数
复制已有的 set:
#include <iostream>
#include <set>
std::set<int> originalSet = {1, 2, 3};
std::set<int> copiedSet(originalSet); // 复制构造
for (const auto& elem : copiedSet) {
std::cout << elem << " "; // 输出:1 2 3
}
迭代器:
// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
set的增删查(无改)
函数介绍:
// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
// 查找val,返回val所在的迭代器,没有找到返回end()
iterator find (const value_type& val);
// 查找val,返回Val的个数
size_type count (const value_type& val) const;
// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除val,val不存在返回0,存在返回1
size_type erase (const value_type& val);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);
// 返回⼤于等val位置的迭代器
iterator lower_bound (const value_type& val) const;
// 返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;
插入操作详解:
- 基本插入
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
// 插入元素
mySet.insert(10);
mySet.insert(20);
mySet.insert(30);
// 打印元素
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出: 10 20 30
return 0;
}
- 插入重复元素
set 只存储唯一的元素,尝试插入重复元素时不会插入成功。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
mySet.insert(10);
mySet.insert(20);
mySet.insert(10); // 重复元素
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出: 10 20
return 0;
}
- 使用 insert 返回值
insert 函数返回一个 pair,其中包含一个迭代器和一个布尔值,布尔值表示插入是否成功。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
auto result = mySet.insert(10);
if (result.second) {
std::cout << "插入成功: " << *result.first << std::endl;
} else {
std::cout << "插入失败: " << *result.first << std::endl;
}
result = mySet.insert(10); // 再次插入同样的元素
if (result.second) {
std::cout << "插入成功: " << *result.first << std::endl;
} else {
std::cout << "插入失败: " << *result.first << std::endl;
}
return 0;
}
- 插入多个元素
可以使用 insert 函数直接插入一个范围的元素。
#include <iostream>
#include <set>
#include <vector>
int main() {
std::set<int> mySet;
std::vector<int> vec = {1, 2, 3, 4, 5};
// 插入整个范围
mySet.insert(vec.begin(), vec.end());
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出: 1 2 3 4 5
return 0;
}
- 插入自定义对象
如果要在 set 中存储自定义对象,需要重载 < 运算符。
#include <iostream>
#include <set>
struct Person {
std::string name;
int age;
// 重载小于运算符以进行比较
bool operator<(const Person& other) const {
return name < other.name; // 按名字排序
}
};
int main() {
std::set<Person> people;
people.insert({"Alice", 30});
people.insert({"Bob", 25});
people.insert({"Charlie", 20});
for (const auto& person : people) {
std::cout << person.name << " " << person.age << std::endl;
}
return 0;
}
- 处理空插入
尝试插入空元素(如默认构造的对象)时,可以用 set 来确保元素的唯一性。
#include <iostream>
#include <set>
int main() {
std::set<std::string> mySet;
mySet.insert(""); // 插入空字符串
mySet.insert("Hello");
mySet.insert(""); // 尝试插入重复的空字符串
for (const auto& str : mySet) {
std::cout << str << std::endl; // 输出空行和 "Hello"
}
return 0;
}
删除操作:
- 使用 erase 删除单个元素
可以使用 erase 函数删除指定的元素。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5};
// 删除元素 3
mySet.erase(3);
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出: 1 2 4 5
return 0;
}
- 使用 erase 删除范围内的元素
erase 也可以用于删除范围内的元素。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5, 6};
// 删除元素范围 [3, 5)
mySet.erase(mySet.find(3), mySet.find(5));
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出: 1 2 5 6
return 0;
}
- 删除所有元素
可以使用 clear 函数清空 set 中的所有元素。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5};
// 清空集合
mySet.clear();
std::cout << "集合大小: " << mySet.size() << std::endl; // 输出: 0
return 0;
}
- 使用返回值检查删除结果
使用 erase 删除元素时,可以通过返回值检查实际删除的元素数量。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5};
// 删除元素 3
size_t removedCount = mySet.erase(3);
if (removedCount > 0) {
std::cout << "成功删除元素 3。" << std::endl;
} else {
std::cout << "元素 3 不在集合中。" << std::endl;
}
return 0;
}
- 删除自定义对象
如果 set 中存储的是自定义对象,依然可以使用 erase,前提是重载了 < 运算符。
#include <iostream>
#include <set>
struct Person {
std::string name;
int age;
bool operator<(const Person& other) const {
return name < other.name; // 按名字排序
}
};
int main() {
std::set<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 20}};
// 删除 Bob
people.erase({"Bob", 25});
for (const auto& person : people) {
std::cout << person.name << " " << person.age << std::endl;
}
// 输出: Alice 30
// Charlie 20
return 0;
}
特别函数操作案例:
- lower_bound
功能: 返回一个迭代器,指向容器中第一个大于或等于给定值的元素。如果所有元素都小于该值,则返回 end()。
用法:
iterator lower_bound(const Key& key);
示例:
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 4, 5, 6};
// 查找大于或等于 4 的第一个元素
auto it = mySet.lower_bound(4);
if (it != mySet.end()) {
std::cout << "lower_bound(4): " << *it << std::endl; // 输出: 4
} else {
std::cout << "没有找到大于或等于 4 的元素。" << std::endl;
}
// 查找大于或等于 3 的第一个元素
it = mySet.lower_bound(3);
if (it != mySet.end()) {
std::cout << "lower_bound(3): " << *it << std::endl; // 输出: 4
}
// 查找大于或等于 7 的第一个元素
it = mySet.lower_bound(7);
if (it != mySet.end()) {
std::cout << "lower_bound(7): " << *it << std::endl;
} else {
std::cout << "没有找到大于或等于 7 的元素。" << std::endl; // 输出: 没有找到大于或等于 7 的元素。
}
return 0;
}
- upper_bound
功能: 返回一个迭代器,指向容器中第一个大于给定值的元素。如果所有元素都小于或等于该值,则返回 end()。
用法:
iterator upper_bound(const Key& key);
示例:
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 4, 5, 6};
// 查找大于 4 的第一个元素
auto it = mySet.upper_bound(4);
if (it != mySet.end()) {
std::cout << "upper_bound(4): " << *it << std::endl; // 输出: 5
} else {
std::cout << "没有找到大于 4 的元素。" << std::endl;
}
// 查找大于 2 的第一个元素
it = mySet.upper_bound(2);
if (it != mySet.end()) {
std::cout << "upper_bound(2): " << *it << std::endl; // 输出: 4
}
// 查找大于 6 的第一个元素
it = mySet.upper_bound(6);
if (it != mySet.end()) {
std::cout << "upper_bound(6): " << *it << std::endl;
} else {
std::cout << "没有找到大于 6 的元素。" << std::endl; // 输出: 没有找到大于 6 的元素。
}
return 0;
}
总结
lower_bound: 返回第一个大于或等于给定值的元素。
upper_bound: 返回第一个大于给定值的元素。
查找函数:
- 查找元素的函数
在 set 中,主要的查找函数包括 find、count。我们将逐一介绍这些函数的用法及示例。
1,find
find 函数用于查找一个特定的元素。如果找到该元素,返回一个指向该元素的迭代器;如果未找到,返回 end() 迭代器。
用法:
iterator find(const Key& key);
示例:
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5};
// 查找元素 3
auto it = mySet.find(3);
if (it != mySet.end()) {
std::cout << "找到了元素: " << *it << std::endl; // 输出: 找到了元素: 3
} else {
std::cout << "未找到元素。" << std::endl;
}
// 查找不存在的元素 6
it = mySet.find(6);
if (it == mySet.end()) {
std::cout << "未找到元素 6。" << std::endl; // 输出: 未找到元素 6。
}
return 0;
}
2, count
count 函数用于统计某个元素在 set 中出现的次数。由于 set 中的元素是唯一的,因此 count 只会返回 0 或 1。
用法:
size_type count(const Key& key) const;
示例:
#include <iostream>
#include <set>
int main() {
std::set<int> mySet = {1, 2, 3, 4, 5};
// 统计元素 3 的出现次数
int count = mySet.count(3);
std::cout << "元素 3 出现了 " << count << " 次。" << std::endl; // 输出: 元素 3 出现了 1 次。
// 统计不存在的元素 6
count = mySet.count(6);
std::cout << "元素 6 出现了 " << count << " 次。" << std::endl; // 输出: 元素 6 出现了 0 次。
return 0;
}
map的定义与使用
map的介绍
map的声明如下,Key就是map底层关键字的类型,T是map底层valu的类型,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数,map底层存储数据的内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是⽤红⿊树实现,增删查改效率是O(logN) ,迭代器遍历是⾛的中序,所以是按key有序顺序遍历的。
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > //
map::allocator_type
> class map;
pair类型介绍
在 C++ 中,std::pair 是一个用于存储两个相关联数据项的模板类。它定义< utility >头文件中,可以方便地将两个不同类型的值组合在一起。以下是关于 pair 类型的详细介绍。
map底层的红⿊树节点中的数据,使⽤pair<Key,T>存储键值对数据。
typedef pair<const Key, T> value_type;
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
template<class U, class V>
pair (const pair<U,V>& pr): first(pr.first), second(pr.second)
{}
};
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{
return ( pair<T1,T2>(x,y) );
}
- 定义和用法
std::pair 的基本定义如下:
template <class T1, class T2>
struct pair {
T1 first; // 第一个元素
T2 second; // 第二个元素
// 构造函数
pair();
pair(const T1& a, const T2& b);
...
};
- 构造和初始化
可以通过不同的方式初始化 std::pair:
#include <iostream>
#include <utility> // 需要包含此头文件
int main() {
// 使用默认构造函数
std::pair<int, double> p1;
// 使用参数构造
std::pair<int, double> p2(1, 3.14);
// 直接初始化
std::pair<std::string, int> p3{"apple", 5};
// 输出
std::cout << "p2: (" << p2.first << ", " << p2.second << ")\n";
std::cout << "p3: (" << p3.first << ", " << p3.second << ")\n";
return 0;
}
- 成员访问
可以通过 first 和 second 成员访问 pair 中的值:
std::cout << "p2.first: " << p2.first << ", p2.second: " << p2.second << std::endl;
- 赋值和比较
std::pair 支持赋值操作和比较操作:
std::pair<int, double> p4;
p4 = p2; // 赋值
if (p2 == p4) {
std::cout << "p2 和 p4 相等\n";
}
- 应用场景
返回多个值:可以用 std::pair 返回多个相关的值,例如在函数中返回一个状态码和结果。
std::pair<int, std::string> getData() {
return {200, "OK"};
}
键值对:在 std::map 和 std::set 中,std::pair 常被用于存储键值对。
-
注意事项
类型:std::pair 中的两个类型可以相同,也可以不同。
自定义比较:对于排序或存储在关联容器中,可能需要自定义比较函数。 -
例子
以下是一个使用 std::pair 的完整示例,展示了如何在 C++ 中使用它:
#include <iostream>
#include <utility>
#include <vector>
int main() {
// 创建一组学生的姓名和分数
std::vector<std::pair<std::string, int>> students = {
{"Alice", 90},
{"Bob", 85},
{"Charlie", 95}
};
// 输出学生信息
for (const auto& student : students) {
std::cout << student.first << " 的分数是 " << student.second << std::endl;
}
return 0;
}
map的构造函数与迭代器
map的⽀持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序;⽀持迭代器就意味着⽀持范围for,map⽀持修改value数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
// empty (1) ⽆参默认构造
explicit map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// range (2) 迭代器区间构造
template <class InputIterator>
map (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());
// copy (3) 拷⻉构造
map (const map& x);
// initializer list (5) initializer 列表构造
map (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
map的增删查
map增接口,插⼊的pair键值对数据,跟set所有不同,但是查和删的接口只⽤关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代还可以修改value
Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>
// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
// 查找k,返回k所在的迭代器,没有找到返回end()
iterator find (const key_type& k);
// 查找k,返回k的个数
size_type count (const key_type& k) const;
// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除k,k存在返回0,存在返回1
size_type erase (const key_type& k);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);
// 返回⼤于等k位置的迭代器
iterator lower_bound (const key_type& k);
// 返回⼤于k位置的迭代器
const_iterator lower_bound (const key_type& k) const;
增删查的注意事项:
Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>
// 查找k,返回k所在的迭代器,没有找到返回end(),如果找到了通过iterator可以修改key对应的
mapped_type值
iterator find (const key_type& k);
// ⽂档中对insert返回值的说明
// The single element versions (1) return a pair, with its member pair::first
set to an iterator pointing to either the newly inserted element or to the
element with an equivalent key in the map. The pair::second element in the pair
is set to true if a new element was inserted or false if an equivalent key
already existed.
// insert插⼊⼀个pair<key, T>对象
// 1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是key所在结点的迭代器,second是false
// 2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是新插⼊key所在结点的迭代器,second是true
// 也就是说⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭
代器
// 那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现
operator[]
// 需要注意的是这⾥有两个pair,不要混淆了,⼀个是map底层红⿊树节点中存的pair<key, T>,另
⼀个是insert返回值pair<iterator,bool>
pair<iterator,bool> insert (const value_type& val);
mapped_type& operator[] (const key_type& k);
// operator的内部实现
mapped_type& operator[] (const key_type& k)
{
// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能
// 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能
pair<iterator, bool> ret = insert({ k, mapped_type() });
iterator it = ret.first;
return it->second;
}
插入函数:
- 定义和包含头文件
首先,确保包含
#include <map>
- 插入元素的方法
2.1 使用 insert()
insert() 方法可以插入单个元素或多个元素。
插入单个元素:
std::map<int, std::string> myMap;
myMap.insert(std::make_pair(1, "Apple")); // 使用 make_pair
myMap.insert({2, "Banana"}); // 使用初始化列表
插入多个元素:
std::map<int, std::string> myMap;
myMap.insert({{1, "Apple"}, {2, "Banana"}, {3, "Cherry"}});
2.2 使用 emplace()
emplace() 方法更高效,因为它可以直接在 map 中构造元素:
myMap.emplace(4, "Date"); // 直接插入
2.3 使用下标运算符 []
下标运算符也可以插入元素,若键不存在,会自动创建一个新元素:
myMap[5] = "Elderberry"; // 若 5 不存在,则插入
- 返回值
insert() 返回一个 pair,其中 first 是插入的位置的迭代器,second 是一个布尔值,表示插入是否成功(如果键已存在则为 false)。
auto result = myMap.insert({1, "Apple"});
if (!result.second) {
std::cout << "Key already exists.\n";
}
emplace() 和下标运算符同样具有类似的返回值特性。
4. 注意事项
唯一性:map 中的键是唯一的,插入相同的键会导致覆盖旧值。
有序性:map 会根据键的顺序自动排序。
性能:emplace() 通常比 insert() 更高效,特别是在插入复杂对象时。
- 示例代码
以下是一个完整示例,展示 map 的插入操作:
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> myMap;
// 使用 insert()
myMap.insert(std::make_pair(1, "Apple"));
myMap.insert({2, "Banana"});
// 使用 emplace()
myMap.emplace(3, "Cherry");
// 使用下标运算符
myMap[4] = "Date";
// 输出所有元素
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
删除函数:
std::map 提供了多种删除元素的方法。以下是有关删除函数的详细说明:
- 使用 erase()
erase() 是最常用的删除函数,可以通过键或迭代器删除元素。
1.1 根据键删除
可以使用键直接删除元素:
std::map<int, std::string> myMap;
// 假设 myMap 中已有元素
myMap.erase(1); // 删除键为 1 的元素
1.2 根据迭代器删除
可以通过迭代器删除特定位置的元素:
auto it = myMap.find(2);
if (it != myMap.end()) {
myMap.erase(it); // 删除键为 2 的元素
}
1.3 删除一段元素
可以使用两个迭代器删除范围内的元素:
myMap.erase(myMap.find(1), myMap.find(4)); // 删除键在 1 到 4 之间的元素(不包括 4)
- 返回值
erase() 返回值:根据删除方式不同,返回值如下:
根据键删除时,返回删除的元素数量(成功删除返回 1,未找到返回 0
)。
根据迭代器删除时,返回被删除元素之后的迭代器。 - 注意事项
键的存在性:在删除前可以使用 find() 检查键是否存在,以避免不必要的删除操作。
迭代器失效:删除操作会使得指向被删除元素的迭代器失效,因此在使用迭代器删除后,应该更新迭代器。
不会抛出异常:在删除操作中,erase() 不会因为元素不存在而抛出异常。 - 示例代码
以下是一个完整的示例,展示如何使用 erase() 删除 map 中的元素:
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> myMap = {
{1, "Apple"},
{2, "Banana"},
{3, "Cherry"},
{4, "Date"}
};
// 删除键为 2 的元素
myMap.erase(2);
// 使用迭代器删除键为 3 的元素
auto it = myMap.find(3);
if (it != myMap.end()) {
myMap.erase(it);
}
// 输出剩余元素
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
查找函数:
- 使用 find()
find() 函数用于查找 map 中是否存在指定的键。
示例用法:
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> myMap = {
{1, "Apple"},
{2, "Banana"},
{3, "Cherry"},
{4, "Date"}
};
int keyToFind = 2;
auto it = myMap.find(keyToFind);
if (it != myMap.end()) {
std::cout << "Found: " << it->first << " => " << it->second << std::endl;
} else {
std::cout << "Key " << keyToFind << " not found." << std::endl;
}
return 0;
}
- 使用 count()
count() 函数返回指定键的元素数量。由于 std::map 中的键是唯一的,因此返回值要么是 0(键不存在),要么是 1(键存在)。
示例用法:
int count = myMap.count(keyToFind);
if (count > 0) {
std::cout << "Key " << keyToFind << " exists." << std::endl;
} else {
std::cout << "Key " << keyToFind << " does not exist." << std::endl;
}
- 使用下标运算符 []
虽然下标运算符不仅用于查找,还可用于插入和访问元素,但在查找时也可以使用。若键不存在,则会插入一个默认值。
示例用法:
std::string value = myMap[keyToFind]; // 如果 keyToFind 不存在,则插入默认值
std::cout << "Value: " << value << std::endl;
注意:如果使用 [] 运算符查找一个不存在的键,map 会自动插入该键,并用其类型的默认值初始化。
- 使用 lower_bound() 和 upper_bound()
这两个函数用于获取指向特定键的迭代器,可以用于范围查找。
lower_bound() 返回第一个不小于给定键的元素的迭代器。
upper_bound() 返回第一个大于给定键的元素的迭代器。
示例用法:
auto lb = myMap.lower_bound(3);
if (lb != myMap.end()) {
std::cout << "Lower bound of 3: " << lb->first << " => " << lb->second << std::endl;
}
auto ub = myMap.upper_bound(3);
if (ub != myMap.end()) {
std::cout << "Upper bound of 3: " << ub->first << " => " << ub->second << std::endl;
}
总结
find(): 返回指向指定键的迭代器,如果键不存在则返回 end()。
count(): 返回指定键的个数(0或1)。
下标运算符 []: 访问或插入元素,若键不存在则插入默认值。
lower_bound() 和 upper_bound(): 用于范围查找,返回迭代器。
原文地址:https://blog.csdn.net/qq_34434522/article/details/142521643
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!