关联式容器——map与set

news2024/9/28 4:07:25

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.coloradmin.cn/o/2167377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ego微商小程序项目实战4.0【环境搭建】

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

自动化测试框架集成:将Selenium集成到pytest与unittest中

目录 引言 一、Selenium简介 二、Selenium与pytest的集成 1. 安装pytest和Selenium 2. 编写测试用例 3. 运行测试 三、Selenium与unittest的集成 1. 编写测试类 2. 运行测试 四、Selenium自动化测试的最佳实践 1. 使用Page Object模式 2. 合理利用等待机制 3. 跨浏…

vioovi视与视标准工时工具与ECRS工时分析软件:精益生产的新纪元

在当今快速变化的市场环境中&#xff0c;企业面临着前所未有的挑战&#xff0c;其中成本控制与效率提升成为制约其发展的关键因素。传统的标准工时工具在应对这些挑战时显得力不从心&#xff0c;其局限性日益凸显。而vioovi视与视标准工时工具的出现&#xff0c;则为企业实现精…

西门子触摸屏下载时提示缺少面板映像的解决方法汇总

西门子触摸屏下载时提示缺少面板映像的解决方法汇总 使用V15/V15.1/V16/V17/V18/V19下载精智(Comfort)系列屏的项目时有时会提示“由于缺少面板映像,下载失败。请安装缺少的面板映像”,造成上述异常的主要原因是V15/V15.1/V16/V17/V18/V19的软件缺少对应的映像文件。常见问题…

《Linux从小白到高手》开篇:脱胎换骨之为什么要深度学习Linux?

List item 这一篇字数比较多&#xff0c;可能会比较枯燥。但是&#xff0c;如果你想学习Linux或者你想进一步提升自己的Linux专业水平&#xff0c;那么&#xff0c;还是请你坚持看完。这倒不是说文笔有多好&#xff0c;而是作为一个学习并使用了Linux 十多年的老司机&#xff…

PHP Swoole 基本使用

背景 在项目中&#xff0c;我们使用了PHP语言&#xff0c;但由于存在长耗时的任务&#xff0c;所以需要服务器端异步响应。为了实现异步响应&#xff0c;我们有多种方案可选&#xff0c;包括MQ&#xff08;消息队列&#xff09;、fsocket&#xff08;文件套接字&#xff09;、…

【Redis】渐进式遍历 数据库管理命令 RESP协议

目录 渐进式遍历 scan 数据库管理命令 切换数据库 获取当前数据库key的个数 删除当前数据库所有的key 删除所有数据库中所有的key RESP协议 渐进式遍历 Redis使用scan命令进行渐进式遍历键&#xff0c;进而解决直接使用keys获取键时可能出现的阻塞问题&#xff08;因为…

多旋翼无人机光伏发电站吊运技术详解

随着清洁能源技术的快速发展&#xff0c;光伏发电站作为可再生能源的重要组成部分&#xff0c;其建设与维护对效率、安全性和成本控制的要求日益提高。多旋翼无人机光伏发电站吊运技术应运而生&#xff0c;该技术利用多旋翼无人机强大的垂直起降能力、灵活的飞行控制以及高效的…

CSS中的字体样式、文本样式、列表样式以及背景和渐变

一、字体样式和文本样式 1.span标签 span标签的作用&#xff1a;能让某几个文字或者是词语凸显出来 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-…

【深度学习基础模型】门控循环单元 (Gated Recurrent Units, GRU)详细理解并附实现代码。

【深度学习基础模型】门控循环单元 (Gated Recurrent Units, GRU) 【深度学习基础模型】门控循环单元 (Gated Recurrent Units, GRU) 文章目录 【深度学习基础模型】门控循环单元 (Gated Recurrent Units, GRU)1.门控循环单元 (Gated Recurrent Units, GRU) 原理详解1.1 GRU 概…

一个任务的一辈子

总览 孕育&#xff1a;这一步是生命的起源&#xff0c;对应"任务"就是&#xff1a;申办人因为办理业务而发起一个流程。这是任务产生的摇篮。 任务的使命就是为了完成业务;生产&#xff1a;这是新生命产生的过程&#xff0c;对应"任务"就是&#xff1a;任务…

IT运维挑战与对策:构建高效一体化运维管理体系

在当今数字化时代&#xff0c;IT运维作为企业运营的核心支撑&#xff0c;其重要性不言而喻。然而&#xff0c;随着业务规模的扩大和技术的不断革新&#xff0c;IT运维团队面临着前所未有的挑战。本文旨在深度剖析当前IT运维中存在的主要问题&#xff0c;并探索一体化解决方案&a…

1500PLC使用EPOS控制伺服电机

硬件配置与参数 硬件配置 名称 型号 数量 PLC 1512C-1 PN 1个 伺服放大器 V90 PN 1个 伺服电机 SIMOTICS 1个 V90 PN伺服驱动器&#xff1a; 伺服驱动器硬件参数 使用软件&#xff1a;V-ASSISTANT 软件连接时可选择USB连接或者Ethernet连接&#xff0c;根据实际…

【ComfyUI】生成图细节更清晰——Consistency_Decoder

原文&#xff1a;https://github.com/openai/consistencydecoder comfyui: https://github.com/gameltb/Comfyui_Consistency_Decoder_VAE 博文资料下载&#xff1a;https://pan.baidu.com/s/1SwfA4T6iMsA8IrRrGXm4sg?pwd0925 安装 【秋葉aaaki】comfyui一键运行包 夸克网盘…

Vue下载静态文件

1、需求&#xff1a;将静态文件放在本地&#xff0c;让用户进行下载。 2、文件位置&#xff1a; ① 原生js&#xff1a;直接将文件放在某个目录或者根目录下 ② Vue&#xff1a;将文件放在根目录的public文件夹下面 3、代码示例&#xff1a; const url "/模板.xlsx"…

音视频入门基础:AAC专题(9)——FFmpeg源码中计算AAC裸流每个packet的duration和duration_time的实现

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

前言 动手学深度学习课程安排及介绍

前言 动手学深度学习课程安排及介绍 文章目录 前言 动手学深度学习课程安排及介绍课程预告课程安排深度学习介绍 课程预告 学习深度学习关键是动手。 深度学习是人工智能最热的领域核心是神经网络神经网络是一门语言应该像学习Python/C一样学习深度学习 课程安排 【动手学深…

Mysql 存储List类型的数据

python request 爬到的数据里面有一部分是List&#xff0c;一开始在建表时想当然地使用 create table if not exists demo (id TEXT, short_id TEXT, parent_ids LIST)结果报错syntax error&#xff0c;查半天才发现Mysql里没有LIST这个类型 所以存储一个List只能将List数据…

第十六章 模板与泛型编程

16.1 定义模板 模板是C泛型编程的基础。为模板提供足够的信息&#xff0c;就能生成特定的类或函数。 16.1.1 函数模板 在模板定义中&#xff0c;模板参数列表不能为空。 //T的实际类型在编译时根据compare的使用情况来确定 template <typename T> int compare(const …

乱篇弹(54)让子弹飞

创作者在知乎能挣到钱吗&#xff1f; 芝士平台的答案&#xff1a;“当然能&#xff0c;在知乎&#xff0c;无论是各领域的优秀回答者&#xff0c;还是拥有几百或几千关注者的潜力创作者&#xff0c;甚至是只在知乎创作过几篇回答的新人创作者&#xff0c;都有可能在知乎赚钱 。…