当前位置: 首页 > news >正文

STL之关联容器(map ,set)

1.概述

在 C++ 的标准模板库(STL)的丰富工具集中,setmap作为关联容器,扮演着极为关键的角色,为开发者处理复杂数据场景提供了有力支持。它们拥有一些显著的共性,皆基于红黑树这种自平衡二叉搜索树的数据结构构建,这使得二者在元素的插入、删除和查找操作上,平均时间复杂度均能达到令人满意的 O (log n),高效地满足了各类程序对数据快速处理的需求。不过,setmap又在诸多方面展现出鲜明的个性set专注于构建一个包含唯一元素的集合,主要目的在于高效地判断某个元素是否存在于集合之中;而map则致力于维护键值对的映射关系,能根据特定的键快速检索到与之关联的值。接下来,就让我们全方位、深层次地对比剖析setmap在存储结构、操作方法以及适用场景等维度的异同之处,助力你透彻理解并灵活运用这两种强大的容器,大幅提升 C++ 程序开发的效率与质量 。

2.数据结构

黑红树

  红黑树本质上是一种特殊的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色(红色或黑色)。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

 不过多介绍 它就是一个有序 并且查找删除效率高的 而且是有序的

有序性的体现

  • 节点值的有序排列:在红黑树中,对于任意一个节点,其左子树中所有节点的值都小于该节点的值,而其右子树中所有节点的值都大于该节点的值。这种特性保证了红黑树中的节点是按照节点值的大小有序排列的。

  • 中序遍历有序输出:对红黑树进行中序遍历(即先访问左子树,再访问根节点,最后访问右子树),可以得到一个按节点值从小到大排列的有序序列。例如,在一个存储整数的红黑树中,通过中序遍历可以依次输出从小到大排列的整数。

map

 他把中间的值换成了key-val

set

 就是红黑树 只不过不能有重复的值 而且可以是任意string以及vector这些

3.操作方法

创建

std::set<Key> s; // 创建一个空的 set,Key 是元素类型
std::map<key,val>m;// 创建一个空的 map,Key 是键的类型,T 是值的类型


//区间范围
std::set<key>s(InputIterator first, InputIterator last)
std::map<Key, T> m(InputIterator first, InputIterator last); 

std::set<Key> s1(s2); 
std::map<Key, T> m1(m2);

std::set<Key> s(std::move(s2));
std::map<Key, T> m(std::move(m2));

 解释一下 m的区间范围 ,你得保证要么就是一个map,要么pair<>类型

例子:

#include <iostream>
#include <map>
#include <vector>
#include <utility>

int main() {
    // 创建一个包含键值对的 vector
    std::vector<std::pair<int, std::string>> vec = {
        {1, "apple"},
        {2, "banana"},
        {3, "cherry"}
    };

    // 使用范围构造函数从 vector 初始化 map
    std::map<int, std::string> myMap(vec.begin(), vec.end());

    // 遍历 map 并输出元素
    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

访问

属性

size_type size() const; // 返回 set和map 中元素的数量


bool empty() const; // 判断 set和 map是否为空


iterator find(const Key& k); 
// 在 set 中查找键为 k 的元素或者在map中寻找k-v,如果找到返回指向该元素的迭代器,否则返回 end()

元素之set

#include <iostream>
#include <set>

int main() {
    std::set<int> mySet = {3, 1, 2};

    // 使用范围 for 循环(底层也是迭代器)
    for (const auto& element : mySet) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 使用普通迭代器
    for (auto it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

元素之map

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};

    // 使用范围 for 循环
    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    // 使用普通迭代器
    for (auto it = myMap.begin(); it != myMap.end(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }

    return 0;
}

这里,pair.first 表示键,pair.second 表示值;对于迭代器 it,可以使用 it->first 和 it->second 来分别访问键和值。

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};

    // 使用 [] 运算符
    std::cout << myMap[2] << std::endl;


}

可以使用 [] 运算符

增加

 std::pair<iterator, bool> insert(const value_type& val); 
// 插入范围
template <class InputIterator>
void insert(InputIterator first, InputIterator last)

template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args); 
// 直接在 set和map 中构造元素

//map 使用 [] 运算符插入键值对
 myMap[6] = "six";

std::pair<iterator, bool> 

  • 第一个成员类型为容器的迭代器(iterator),它指向插入的元素或者指向已经存在的具有相同键的元素
  • 第二个成员类型为 bool,表示插入操作是否成功。如果插入的元素是新元素(即容器中原本不存在具有相同键的元素),则返回 true;如果容器中已经存在具有相同键的元素,插入操作不会重复插入该元素,此时返回 false。

关于直接构造

insert 插入方式

当使用 insert 函数插入元素时,通常需要先创建一个完整的对象,然后将这个对象传递给 insert 函数。这可能涉及到对象的构造、拷贝或移动操作。

emplace 函数则允许你直接在容器内部构造对象,它接受的是用于构造对象的参数,而不是一个已经构造好的对象

 例子:

#include <iostream>
#include <map>
#include <string>

class ExpensiveToCopy {
public:
    ExpensiveToCopy(int value, const std::string& str) : data(value), text(str) {
        std::cout << "Constructor called" << std::endl;
    }
    ExpensiveToCopy(const ExpensiveToCopy& other) : data(other.data), text(other.text) {
        std::cout << "Copy constructor called" << std::endl;
    }
    ExpensiveToCopy(ExpensiveToCopy&& other) noexcept : data(other.data), text(std::move(other.text)) {
        std::cout << "Move constructor called" << std::endl;
    }
    int data;
    std::string text;
};

int main() {
    std::map<int, ExpensiveToCopy> myMap;

    std::cout << "Using insert:" << std::endl;
    // 使用 insert 插入元素
    myMap.insert({1, ExpensiveToCopy(10, "hello")});

    std::cout << "\nUsing emplace:" << std::endl;
    // 使用 emplace 插入元素
    myMap.emplace(2, 20, "world");

    return 0;
}

删除

// 删除指定位置的元素
iterator erase(iterator position); 
// 删除指定键的元素
size_type erase(const Key& k); 
// 删除指定范围的元素
iterator erase(iterator first, iterator last); 


// 删除指定位置的键值对
iterator erase(iterator position); 
// 删除指定键的键值对
size_type erase(const Key& k); 
// 删除指定范围的键值对
iterator erase(iterator first, iterator last); 

void clear(); // 清空 set 中的所有元素

 4 应用场景

共性场景

  • 构造开销大的对象插入:当存储的元素或键值对涉及自定义类型,且这些类型的构造、拷贝或移动操作开销较大时,emplace 可直接在容器内部利用传入参数构造对象,避免额外临时对象的创建与复制,减少开销,显著提升性能。
  • 频繁插入操作:在需要大量、频繁地向 std::set 或 std::map 中插入元素的场景下,emplace 每次插入都能避免临时对象的创建和复制,随着插入次数增多,性能优势愈发明显。

各自特点场景

std::set

  • 适用于存储自定义类型元素,通过 emplace 可高效地将元素插入到集合中,利用其直接构造特性维护集合的有序性和元素唯一性。

std::map

  • 复杂键值对存储:当键值对类型复杂,特别是值类型构造开销大时,emplace 能直接在 map 内部构造键值对,提高插入效率。
  • 动态数据插入:在动态生成键值对并插入 map 的场景,如从文件或网络读取数据插入时,emplace 可更高效地处理操作,避免创建临时对象的开销。

5.multimap和multiset

相同点

都是黑红树

不同点:

元素

  • 与 std::map 不同的是,std::multimap 允许存储多个具有相同键的键值对。

  • 与 std::set 不同的是,std::multiset 允许存储重复的元素,即可以有多个元素具有相同的值

应用场景:

map:

  • 一对多映射关系:当需要表示一对多的映射关系时,例如一个学生可以有多个成绩,一个作者可以有多本书等,std::multimap 可以很好地满足需求。
  • 按键分组数据:可以将具有相同键的数据分组存储在 std::multimap 中,方便后续的处理和查询。

set 

         统计元素出现次数:由于 std::multiset 允许存储重复元素,可以方便地统计某个元素在集合中出现的次数,使用 count() 函数即可实现。

          维护有序序列且允许重复:当需要维护一个有序的元素序列,并且允许元素重复时,std::multiset 是一个不错的选择。

相关文章:

  • 【AI 加持下的 Python 编程实战 2_03】第二章:Copilot 辅助编程入门——环境搭建、基本工作流程以及数据分析案例演示(含本地实测)
  • PAT甲级(Advanced Level) Practice 1021 Deepest Root
  • 通达信软件+条件选股+code
  • Atcoder ABC397-D 题解
  • L1-093 猜帽子游戏
  • 如何将一个项目推送到gitlab
  • 注意力机制:让AI拥有黄金七秒记忆的魔法--(自注意力)
  • 机器学习——正则化、欠拟合、过拟合、学习曲线
  • Webpack 前端性能优化全攻略
  • 【实用技巧】如何优雅的批量保存网页快照?
  • CTF题目《SSRFMe》(网鼎杯 2020 玄武组)WriteUp
  • Qlik Sense New Install with Restore
  • 【开源代码解读】AI检索系统R1-Searcher通过强化学习RL激励大模型LLM的搜索能力
  • Word 小黑第36套
  • 探索Maas平台与阿里 QWQ 技术:AI调参的魔法世界
  • 在PowerShell脚本中编辑appsettings.json
  • 鸿蒙(OpenHarmony)开发实现 息屏/亮屏 详情
  • Vue本地开发调试使用Proxy实现接口代理配合Nginx实现瓦片png文件代理,实现本地模拟GIS开发环境
  • 《解锁Netlify:静态网站托管》:此文为AI自动生成
  • 探索 Trossen AI:从 Aloha到智能机器人平台的进化之路
  • 2025年上海市模范集体、劳动模范和先进工作者名单揭晓
  • 早期投资人蜂巧资本清仓泡泡玛特套现超22亿港元,称基金即将到期
  • 经济日报整版聚焦“妈妈岗”:就业路越走越宽,有温度重实效
  • 上任后首访,德国总理与法国总统举行会晤
  • 驱逐行动再加码?特朗普或向利比亚和卢旺达遣送非法移民
  • 应对美政策调整:中国重在开放与创新,维护好数据主权