关联式容器——map与set

map与set

  • map与set的使用
    • 序列式容器与关联式容器概念
      • 序列式容器 (Sequence Containers)
        • 常见的序列式容器:
      • 关联式容器 (Associative Containers)
        • 常见的关联式容器:
    • set的定义与使用
      • set类的介绍
      • set的构造和迭代器
      • set的增删查(无改)
    • map的定义与使用
      • map的介绍
      • pair类型介绍
      • map的构造函数与迭代器
      • map的增删查

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());
  1. 默认构造函数
    创建一个空的 set:
#include <set>

std::set<int> mySet; // 创建一个空的 set
  1. 使用初始列表
    使用初始化列表直接创建 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
}
  1. 使用范围构造函数
    从另一个容器(如 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
}
  1. 使用比较函数的构造函数
    自定义比较规则(降序排列):
#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
}
  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;

插入操作详解

  1. 基本插入
#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;
}
  1. 插入重复元素
    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;
}
  1. 使用 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;
}
  1. 插入多个元素
    可以使用 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;
}
  1. 插入自定义对象
    如果要在 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;
}
  1. 处理空插入
    尝试插入空元素(如默认构造的对象)时,可以用 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;
}

删除操作

  1. 使用 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;
}
  1. 使用 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;
}
  1. 删除所有元素
    可以使用 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;
}
  1. 使用返回值检查删除结果
    使用 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;
}
  1. 删除自定义对象
    如果 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;
}

特别函数操作案例

  1. 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;
}
  1. 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: 返回第一个大于给定值的元素。

查找函数

  1. 查找元素的函数
    在 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) );
}
  1. 定义和用法
    std::pair 的基本定义如下:
template <class T1, class T2>
struct pair {
    T1 first;   // 第一个元素
    T2 second;  // 第二个元素

    // 构造函数
    pair();
    pair(const T1& a, const T2& b);
    ...
};
  1. 构造和初始化
    可以通过不同的方式初始化 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;
}
  1. 成员访问
    可以通过 first 和 second 成员访问 pair 中的值:
std::cout << "p2.first: " << p2.first << ", p2.second: " << p2.second << std::endl;
  1. 赋值和比较
    std::pair 支持赋值操作和比较操作:
std::pair<int, double> p4;
p4 = p2; // 赋值

if (p2 == p4) {
    std::cout << "p2 和 p4 相等\n";
}
  1. 应用场景
    返回多个值:可以用 std::pair 返回多个相关的值,例如在函数中返回一个状态码和结果。
std::pair<int, std::string> getData() {
    return {200, "OK"};
}

键值对:在 std::map 和 std::set 中,std::pair 常被用于存储键值对。

  1. 注意事项
    类型:std::pair 中的两个类型可以相同,也可以不同。
    自定义比较:对于排序或存储在关联容器中,可能需要自定义比较函数。

  2. 例子
    以下是一个使用 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;
}

插入函数

  1. 定义和包含头文件
    首先,确保包含 头文件:
#include <map>
  1. 插入元素的方法
    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 不存在,则插入
  1. 返回值
    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() 更高效,特别是在插入复杂对象时。

  1. 示例代码
    以下是一个完整示例,展示 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 提供了多种删除元素的方法。以下是有关删除函数的详细说明:

  1. 使用 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)
  1. 返回值
    erase() 返回值:根据删除方式不同,返回值如下:
    根据键删除时,返回删除的元素数量(成功删除返回 1,未找到返回 0)。
    根据迭代器删除时,返回被删除元素之后的迭代器。
  2. 注意事项
    键的存在性:在删除前可以使用 find() 检查键是否存在,以避免不必要的删除操作。
    迭代器失效:删除操作会使得指向被删除元素的迭代器失效,因此在使用迭代器删除后,应该更新迭代器。
    不会抛出异常:在删除操作中,erase() 不会因为元素不存在而抛出异常。
  3. 示例代码
    以下是一个完整的示例,展示如何使用 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;
}

查找函数

  1. 使用 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;
}
  1. 使用 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;
}
  1. 使用下标运算符 []
    虽然下标运算符不仅用于查找,还可用于插入和访问元素,但在查找时也可以使用。若键不存在,则会插入一个默认值。

示例用法

std::string value = myMap[keyToFind]; // 如果 keyToFind 不存在,则插入默认值
std::cout << "Value: " << value << std::endl;

注意:如果使用 [] 运算符查找一个不存在的键,map 会自动插入该键,并用其类型的默认值初始化。

  1. 使用 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(): 用于范围查找,返回迭代器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883224.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【多线程】面试高频考点!JUC常见类的详细总结,建议收藏!

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 JUC是“Java Util Concurrency”的缩写&#xff0c;指的是Java并发工具包&#xff0c;它位于java.util.concurrent包及其子包中。JUC包提供了大量用于构建并发应用程序的工具和…

ARM基础

一、ARM ARM公司&#xff08;正式名称为ARM Holdings Ltd.&#xff09;是一家总部位于英国剑桥的半导体和软件设计公司&#xff0c;专注于开发和授权基于ARM架构的处理器技术。 ARM也是一种广泛使用的计算机架构&#xff0c;特别适合于低功耗和高性能的应用。ARM最初由英国的Ac…

【Redis】分布式锁之 Redission

一、基于setnx实现的分布式锁问题 重入问题&#xff1a;获得锁的线程应能再次进入相同锁的代码块&#xff0c;可重入锁能防止死锁。例如在HashTable中&#xff0c;方法用synchronized修饰&#xff0c;若在一个方法内调用另一个方法&#xff0c;不可重入会导致死锁。而synchroni…

WebGL入门(一)绘制一个点

源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><scr…

2-103 基于matlab的光电信号下血氧饱和度计算

基于matlab的光电信号下血氧饱和度计算&#xff0c;光转换成电信号时&#xff0c;由于动脉对光的吸收有变化而其他组织对光的吸收基本不变&#xff0c;得到的信号就可以分为直流DC信号和交流AC信号。提取AC信号&#xff0c;就能反应出血液流动的特点。这种技术叫做光电容积脉搏…

STM32基础学习笔记-Timer定时器面试基础题5

第五章、TIMER 常见问题 1、基本概念&#xff1a;什么是定时器 &#xff1f;作用 &#xff1f;分类 &#xff1f; 2、时基单元 &#xff1f;组成 &#xff1f;计数模式 &#xff1f;溢出条件 &#xff1f; 溢出时间计算 &#xff1f; 3、systick原理 &#xff1f;代码讲解 &…

MODIS/Landsat/Sentinel下载教程详解【常用网站及方法枚举】

⛄前言 在当今快速发展的地球观测时代&#xff0c;遥感技术作为获取地球表面及其环境信息的重要手段&#xff0c;正以前所未有的广度和深度改变着我们对自然界的认知与管理方式。MODIS&#xff08;Moderate-resolution Imaging Spectroradiometer&#xff0c;中分辨率成像光谱…

网络通信——OSI七层模型和TCP/IP模型

OSI模型 一.OSI七层模型 OSI&#xff08;Open System Interconnect&#xff09;七层模型是一种将计算机网络通信协议划分为七个不同层次的标准化框架。每一层都负责不同的功能&#xff0c;从物理连接到应用程序的处理。这种模型有助于不同的系统之间进行通信时&#xff0c;更…

分享课程:VUE数据可视化教程

在当今这个数据驱动的世界中&#xff0c;数据可视化已经成为了一种至关重要的工具&#xff0c;它帮助我们理解复杂的数据集&#xff0c;发现模式、趋势和异常。数据可视化不仅仅是将数字转换成图表&#xff0c;它是一种将数据转化为洞察力的艺术。 1.什么是数据可视化&#xf…

DNS协议解析

DNS协议解析 什么是DNS协议 IP地址&#xff1a;一长串唯一标识网络上的计算机的数字 域名&#xff1a;一串由点分割的字符串名字 网址包含了域名 DNS&#xff1a;域名解析协议 IP>域名 --反向解析 域名>IP --正向解析 域名 由ICANN管理&#xff0c;有级别&#xf…

CVE-2024-46101

前言 自己挖的第一个CVE~ 喜提critical 这里简单说一下。 漏洞简介 GDidees CMS < 3.9.1 的版本&#xff0c;存在一个任意文件上传漏洞。允许登录后的攻击者上传webshell获得网站的权限。 影响版本&#xff1a; GDidees CMS < 3.9.1 &#xff08;其它的我没测。。&am…

二叉树之堆树

堆树是一种完全二叉树&#xff0c;完全二叉树特点&#xff1a;除了最后一层所有层都填满&#xff0c;最后一层节点从左到右排列。堆树分为两种类型&#xff1a;大顶堆和小顶堆。 大顶堆&#xff1a;每个节点的值都大于或等于其子节点的值&#xff0c;根节点是最大值。 小顶堆…

降准降息一揽子措施点燃 A 股激情,4% 大涨之后趋势深度剖析

文章目录 牛回速归原因分析引爆点情绪和信心一根大阳线&#xff0c;千军万马来相见阴霾是否一扫而空还未可知 流动性和增量 潜在隐患等待经济复苏配套政策期待中美关系进展 短期内趋势分析空军短期内仍有余力如何看待第2日的回撤外围 趋势分析结论短期内可能仍有波折中长期会是…

Flink Task 日志文件隔离

Flink Task 日志文件隔离 任务在启动时会先通过 MdcUtils 启动一个 slf4j 的 MDC 环境&#xff0c;然后将 jobId 添加到 slf4j 的 MDC 容器中&#xff0c;随后任务输出的日志都将附带 joid。 MDC 介绍如下&#xff1a; MDC ( Mapped Diagnostic Contexts )&#xff0c;它是一个…

C/C++逆向:循环语句逆向分析

在逆向分析中&#xff0c;循环语句通常会以特定的汇编模式或结构体现出来。常见的循环语句包括 for 循环、while 循环和 do-while 循环。由于不同的编译器会根据代码优化的级别生成不同的汇编代码&#xff0c;分析循环的模式也可能会有所不同。以下是三种常见循环语句的汇编分析…

【C++ Primer Plus习题】17.7

问题: 解答: #include <iostream> #include <vector> #include <string> #include <fstream> #include <algorithm>using namespace std;const int LIMIT 50;void ShowStr(const string& str); void GetStrs(ifstream& fin, vector<…

ShardingSphere 分库分表

中间件 常用中间件 MyCat 是基于 Proxy&#xff0c;它复写了 MySQL 协议&#xff0c;将 Mycat Server 伪装成⼀个 MySQL 数据库客户端所有的jdbc请求都必须要先交给MyCat&#xff0c;再有 MyCat转发到具体的真实服务器缺点是效率偏低&#xff0c;中间包装了⼀层代码⽆侵⼊性…

【刷题3】找到字符串中所有字母异位词、串联所有单词的子串

目录 一、找到字符串中所有字母异位词二、串联所有单词的子串 一、找到字符串中所有字母异位词 题目&#xff1a; 思路&#xff1a; 用一个变量count来统计有效字符的个数。哈希表2统计字符串p的每个字符出现的个数&#xff0c;然后遍历字符串s&#xff0c;先进窗口&#xf…

Unity-物理系统-碰撞检测-物理材质

物理材质的作用&#xff1a;改变碰撞效果 因为碰撞的过程是相互的&#xff0c;所以在碰撞双方都要加相同的物理材质才能实现效果 物理材质创建 参数

微软宣布弃用WSUS,企业用户尽早准备替换方案

微软最近宣布将逐步弃用Windows Server Update Services (WSUS)&#xff0c;不再为其开发新功能&#xff0c;但会继续支持现有的更新和功能。这一决定对企业客户来说影响深远&#xff0c;尤其是那些依赖WSUS来管理大规模Windows设备更新的组织。 对企业客户的影响 安全性与合规…