一文搞懂C++容器篇

一文搞懂C++之容器篇

C++ STL(标准模板库)的核心价值之一就是提供了丰富的容器——即“存储数据的通用结构”,能帮我们快速实现数据的存储、访问、插入、删除等操作,无需重复造轮子。本文将C++常用容器按“功能分类”梳理,从底层实现到实际用法层层拆解,结合代码示例和选型建议,让你彻底搞懂“什么时候用什么容器”。

先明确核心概念:C++容器本质是“模板类”,支持存储不同类型的数据(如int、string、自定义结构体),分为三大类:

  • 序列式容器:按“插入顺序”存储数据,元素有明确的位置关系(如数组、链表);

  • 关联式容器:按“键(key)”排序存储数据,支持快速查找(如字典、有序集合);

  • 容器适配器:基于其他容器封装的“特殊功能容器”(如栈、队列),不直接存储数据。

一、序列式容器:按顺序存储,核心是“位置”

序列式容器的核心是“元素的插入顺序=存储顺序”,访问时依赖“位置索引”或“迭代器遍历”,适合需要保持数据顺序的场景。常用的有4个:vectorlistdequearray

1. vector:动态数组(最常用)

核心特性

  • 底层实现:动态数组(连续内存空间),扩容时会分配新的更大内存,拷贝旧数据后释放旧内存;

  • 访问效率:随机访问极快(O(1),通过索引直接访问);

  • 插入/删除:尾部插入/删除极快(O(1));中间/头部插入/删除慢(O(n),需要移动后续元素);

  • 内存特点:预分配内存(capacity > size),避免频繁扩容;支持预留内存(reserve)优化性能。

C++代码示例(常用操作)

#include<iostream>#include<vector>usingnamespacestd;intmain(){// 1. 初始化vector<int>vec1;// 空vectorvector<int>vec2(5,10);// 5个元素,每个都是10vector<int>vec3={1,2,3,4,5};// 初始化列表(C++11+)vector<int>vec4(vec3.begin(),vec3.end());// 用迭代器拷贝// 2. 插入元素vec1.push_back(6);// 尾部插入(推荐,O(1))vec1.emplace_back(7);// 尾部构造插入(更高效,避免拷贝,C++11+)vec3.insert(vec3.begin()+2,99);// 中间插入(在索引2处插入99,O(n))// 3. 访问元素cout<<vec3[2]<<endl;// 索引访问(无越界检查)cout<<vec3.at(2)<<endl;// at访问(有越界检查,抛出异常)cout<<vec3.front()<<endl;// 访问第一个元素cout<<vec3.back()<<endl;// 访问最后一个元素// 4. 遍历// 方式1:范围for(C++11+,最简洁)for(intval:vec3){cout<<val<<" ";}cout<<endl;// 方式2:迭代器for(vector<int>::iterator it=vec3.begin();it!=vec3.end();++it){cout<<*it<<" ";}cout<<endl;// 5. 删除元素vec3.pop_back();// 尾部删除(O(1))vec3.erase(vec3.begin()+2);// 中间删除(删除索引2的元素,O(n))vec3.clear();// 清空所有元素(size=0,capacity不变)// 6. 内存优化vec3.reserve(10);// 预留10个元素的内存,避免频繁扩容vec3.shrink_to_fit();// 释放多余内存(让capacity=size,C++11+)return0;}

适用场景

适合“频繁随机访问、尾部插入/删除”的场景,如:存储检测框坐标、传感器采集的时序数据、需要快速索引的数据列表。工程中最常用的容器,优先考虑vector

2. list:双向链表

核心特性

  • 底层实现:双向链表(非连续内存,每个节点存储数据和前后指针);

  • 访问效率:随机访问极慢(O(n),必须遍历查找);

  • 插入/删除:任意位置插入/删除极快(O(1),只需修改节点指针,无需移动元素);

  • 内存特点:每个节点额外存储两个指针,内存开销比vector大;无扩容问题。

关键操作示例(区别于vector)

#include<list>usingnamespacestd;intmain(){list<int>lst={1,2,3,4};// 1. 任意位置插入/删除(高效)autoit=lst.begin();advance(it,2);// 迭代器移动到第3个元素(只能通过advance移动,无索引)lst.insert(it,99);// 在位置2插入99(O(1))lst.erase(it);// 删除当前迭代器指向的元素(O(1))// 2. 链表特有操作lst.push_front(0);// 头部插入(O(1),vector无此操作)lst.pop_front();// 头部删除(O(1),vector无此操作)lst.reverse();// 反转链表(O(n))lst.sort();// 排序(O(nlogn),底层是归并排序)lst.unique();// 去重(需先排序,O(n))// 3. 遍历(只能用迭代器或范围for,无索引)for(intval:lst){cout<<val<<" ";}return0;}

适用场景

适合“频繁在任意位置插入/删除”的场景,如:需要频繁调整顺序的任务列表、数据频繁增删的缓存(如LRU缓存的底层可基于list实现)。避免用于需要随机访问的场景

3. deque:双端队列

核心特性

  • 底层实现:“分段连续内存”(多个连续内存块,用指针数组管理);

  • 访问效率:随机访问较快(O(1),但比vector慢,需要先定位内存块);

  • 插入/删除:头部和尾部插入/删除极快(O(1));中间插入/删除慢(O(n));

  • 内存特点:无扩容拷贝问题(新增内存块即可),内存利用率比vector高。

适用场景

适合“频繁头部+尾部插入/删除”的场景,如:队列的底层实现、滑动窗口算法的数据存储。日常使用频率低于vector和list,只有需要“双端快速操作”时才考虑。

4. array:固定大小数组(C++11+)

核心特性

  • 底层实现:静态数组(连续内存,大小编译时确定,不可修改);

  • 访问效率:随机访问最快(O(1),无动态扩容开销);

  • 插入/删除:不支持动态插入/删除(大小固定);

  • 对比普通数组:比C风格数组(int arr[5])更安全(支持边界检查、迭代器),更易用。

代码示例

#include<array>usingnamespacestd;intmain(){array<int,5>arr={1,2,3,4,5};// 大小5固定,编译时确定arr[0]=10;cout<<arr.at(2)<<endl;// 边界检查cout<<arr.size()<<endl;// 大小固定为5// 遍历for(intval:arr){cout<<val<<" ";}return0;}

适用场景

适合“数据量固定、需要极致访问效率”的场景,如:存储固定长度的配置参数、嵌入式开发中的固定大小缓冲区。不适合数据量动态变化的场景

二、关联式容器:按key排序,核心是“快速查找”

关联式容器的核心是“键(key)”——元素按key的大小排序存储,支持通过key快速查找(O(logn)),无需遍历。分为“有序关联式”和“无序关联式”两类,有序的底层是红黑树(平衡二叉树),无序的底层是哈希表。

1. 有序关联式容器:set/multiset/map/multimap

共同特性:底层是红黑树,元素按key升序排序(默认),支持自定义排序规则;查找、插入、删除的时间复杂度都是O(logn)。

(1)set:有序唯一集合

  • 核心特点:存储“唯一的key”(不允许重复元素),按key排序;

  • 本质:key=value(存储的元素就是key本身);

  • 常用操作:插入、删除、查找(按key)、去重、排序。

#include<set>usingnamespacestd;intmain(){// 1. 初始化(默认升序)set<int>s={3,1,4,1,2};// 自动去重+排序,结果:1,2,3,4// 降序排序(自定义比较规则)set<int,greater<int>>s_desc={3,1,4,2};// 结果:4,3,2,1// 2. 插入s.insert(5);// 插入5,O(logn);若插入重复元素,无效果// 3. 查找autoit=s.find(3);// 按key查找,返回迭代器(O(logn))if(it!=s.end()){cout<<"找到:"<<*it<<endl;}// 4. 统计元素个数(因为唯一,只能是0或1)cout<<s.count(1)<<endl;// 输出1// 5. 删除s.erase(2);// 按key删除(O(logn))s.erase(it);// 按迭代器删除(O(1))// 6. 遍历(有序)for(intval:s){cout<<val<<" ";// 输出1 3 4 5}return0;}

(2)multiset:有序可重复集合

核心区别于set:允许重复key;count()方法返回元素的重复次数;erase(key)会删除所有重复元素。

#include<set>usingnamespacestd;intmain(){multiset<int>ms={3,1,4,1,2,1};// 排序后:1,1,1,2,3,4cout<<ms.count(1)<<endl;// 输出3(重复3次)// 删除所有key=1的元素ms.erase(1);// 只删除一个key=2的元素(先find找到迭代器)autoit=ms.find(2);if(it!=ms.end()){ms.erase(it);}return0;}

(3)map:有序唯一键值对

  • 核心特点:存储“key-value键值对”,key唯一且有序;

  • 本质:通过key快速查找value(O(logn)),类似字典;

  • 常用操作:插入键值对、按key查找value、遍历键值对。

#include<map>#include<string>usingnamespacestd;intmain(){// 1. 初始化(默认key升序)map<string,int>dict={{"apple",1},{"banana",2},{"orange",3}};// 2. 插入键值对dict.insert(pair<string,int>("grape",4));// 方式1:pairdict.emplace("pear",5);// 方式2:emplace(更高效,C++11+)dict["watermelon"]=6;// 方式3:[]运算符(不存在则插入,存在则修改)// 3. 查找value// 方式1:find(推荐,先判断是否存在)autoit=dict.find("banana");if(it!=dict.end()){cout<<"banana: "<<it->second<<endl;// it->first是key,it->second是value}// 方式2:[]运算符(注意:不存在则插入默认值0)cout<<"grape: "<<dict["grape"]<<endl;// 4. 遍历键值对for(auto&[key,val]:dict){// C++17+结构化绑定,简洁cout<<key<<": "<<val<<endl;}// 兼容旧版本:// for (map<string, int>::iterator it = dict.begin(); it != dict.end(); ++it) {// cout << it->first << ": " << it->second << endl;// }// 5. 删除dict.erase("orange");// 按key删除(O(logn))dict.erase(it);// 按迭代器删除(O(1))return0;}

(4)multimap:有序可重复键值对

核心区别于map:允许重复key;通过equal_range()获取所有相同key的键值对。

#include<map>usingnamespacestd;intmain(){multimap<string,int>mmap;mmap.emplace("apple",1);mmap.emplace("apple",2);mmap.emplace("banana",3);// 获取所有key=apple的键值对autorange=mmap.equal_range("apple");for(autoit=range.first;it!=range.second;++it){cout<<it->first<<": "<<it->second<<endl;// 输出apple:1、apple:2}return0;}

2. 无序关联式容器:unordered_set/unordered_multiset/unordered_map/unordered_multimap(C++11+)

共同特性:底层是哈希表(hash table),元素无序;查找、插入、删除的平均时间复杂度是O(1),最坏O(n)(哈希冲突严重时);比有序关联式容器更快,但无序。

用法几乎和有序版本一致,核心区别:

  • 无序:不支持排序,遍历结果是随机的;

  • 不支持自定义排序规则,但支持自定义哈希函数;

  • 不支持lower_bound()、upper_bound()等有序相关的迭代器操作;

  • 效率更高:平均O(1)的操作效率,适合高频查找场景。

核心示例(unordered_map)

#include<unordered_map>usingnamespacestd;intmain(){unordered_map<string,int>u_dict={{"apple",1},{"banana",2}};// 插入/查找/删除用法和map一致,但无序u_dict["grape"]=3;autoit=u_dict.find("banana");if(it!=u_dict.end()){cout<<it->second<<endl;}// 遍历(结果随机,不排序)for(auto&[key,val]:u_dict){cout<<key<<": "<<val<<endl;// 可能输出banana:2、apple:1、grape:3}return0;}

有序vs无序关联式容器选型

  • 需要有序+唯一:用set/map;

  • 需要有序+可重复:用multiset/multimap;

  • 不需要有序,追求高效查找:用unordered_set/unordered_map(工程中更常用);

  • 自定义类型作为key:有序容器需要重载<运算符;无序容器需要自定义哈希函数和==运算符。

三、容器适配器:封装已有容器,核心是“特殊功能”

容器适配器不直接存储数据,而是“封装”其他容器(如vector、deque),提供特定的操作接口(如栈的先进后出)。常用的有3个:stack、queue、priority_queue。

1. stack:栈(先进后出,LIFO)

  • 底层默认容器:deque(可指定为vector或list);

  • 核心操作:push(入栈)、pop(出栈)、top(访问栈顶);

  • 不支持迭代器遍历(只能访问栈顶)。

#include<stack>usingnamespacestd;intmain(){stack<int>st;// 默认deque为底层容器// stack<int, vector<int>> st; // 指定vector为底层容器st.push(1);st.push(2);st.push(3);cout<<st.top()<<endl;// 访问栈顶:3st.pop();// 出栈:删除3cout<<st.top()<<endl;// 2cout<<st.size()<<endl;// 2cout<<st.empty()<<endl;// falsereturn0;}

2. queue:队列(先进先出,FIFO)

  • 底层默认容器:deque;

  • 核心操作:push(入队)、pop(出队)、front(访问队首)、back(访问队尾);

  • 不支持迭代器遍历。

#include<queue>usingnamespacestd;intmain(){queue<int>q;q.push(1);q.push(2);q.push(3);cout<<q.front()<<endl;// 队首:1cout<<q.back()<<endl;// 队尾:3q.pop();// 出队:删除1cout<<q.front()<<endl;// 2return0;}

3. priority_queue:优先队列(大顶堆/小顶堆)

  • 底层默认容器:vector(基于堆结构实现);

  • 核心特性:每次出队的是“优先级最高”的元素(默认大顶堆,最大元素优先);

  • 核心操作:push(入队)、pop(出队)、top(访问优先级最高元素)。

#include<queue>usingnamespacestd;intmain(){// 1. 默认大顶堆(最大元素优先)priority_queue<int>pq_max;pq_max.push(3);pq_max.push(1);pq_max.push(4);cout<<pq_max.top()<<endl;// 4(最大)pq_max.pop();cout<<pq_max.top()<<endl;// 3// 2. 小顶堆(最小元素优先,自定义比较规则)priority_queue<int,vector<int>,greater<int>>pq_min;pq_min.push(3);pq_min.push(1);pq_min.push(4);cout<<pq_min.top()<<endl;// 1(最小)pq_min.pop();cout<<pq_min.top()<<endl;// 3return0;}

适用场景

  • stack:函数调用栈、表达式求值、回溯算法(如迷宫求解);

  • queue:任务队列、消息队列、广度优先搜索(BFS);

  • priority_queue:TopK问题(如取前10个置信度最高的检测框)、贪心算法、堆排序。

四、C++容器核心选型指南(工程实战必备)

容器选型的核心是“匹配场景的操作需求”,优先考虑“效率+易用性”,以下是高频场景的选型建议:

1. 优先选vector的场景

• 频繁随机访问(用索引访问);• 频繁尾部插入/删除;• 数据量动态变化,且无频繁中间增删;• 不确定用什么容器时,先试vector(兼容性最好,工程最常用)。

2. 选list的场景

• 频繁在任意位置插入/删除;• 不需要随机访问;• 数据量动态变化,且增删位置不固定(如任务列表)。

3. 选关联式容器的场景

• 需快速查找(按key):优先unordered_map/unordered_set(O(1));• 需有序+查找:选map/set(O(logn));• 需键值对存储:选map/unordered_map;• 需去重:选set/unordered_set;• 需重复key:选multiset/multimap。

4. 选容器适配器的场景

• 先进后出:stack;• 先进先出:queue;• 优先级排序:priority_queue。

5. 避坑指南

  • vector迭代器失效:扩容、中间插入/删除时,迭代器会失效,避免保存过期迭代器;

  • unordered容器的哈希冲突:自定义类型作为key时,需确保哈希函数设计合理,避免冲突导致效率下降;

  • 容器的拷贝开销:大容器拷贝时用引用(&)或移动语义(move,C++11+),避免浅拷贝;

  • 内存管理:vector用reserve()预留内存,避免频繁扩容;不用的容器及时clear(),或用局部变量自动释放。

五、总结

C++容器的核心是“按需选择”:序列式容器侧重“顺序存储”,适合按位置操作;关联式容器侧重“key查找”,适合快速检索;容器适配器侧重“特殊功能”,适合特定操作规则。

工程中最常用的容器组合是:vector(基础存储)+unordered_map(快速查找)+priority_queue(TopK问题)。掌握这些容器的核心特性和用法,就能覆盖80%以上的开发场景。

最后记住:不要过度追求“最复杂的容器”,简单的容器(如vector)往往更易维护、兼容性更好。只有当简单容器无法满足效率需求时,再考虑更复杂的容器(如list、unordered_map)。

(注:文档部分内容可能由 AI 生成)

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

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

相关文章

2026雅思网上辅导口碑排行榜:五大机构深度测评及高分提分方案解析

结合2026年雅思考试命题趋势与全国教育机构调研数据,本次针对雅思网上辅导开展全面深度测评,聚焦提分效果、个性化方案、性价比等核心维度,梳理出靠谱实用的机构排行榜。雅思备考中,考生常面临基础薄弱无从下手、单…

全国雅思培训机构深度测评TOP5:权威榜单助力高效选课,精准提分不踩坑

雅思培训选课是众多考生及家长的核心难题,尤其北京海淀区、上海徐汇区、广州天河区、深圳南山区、成都锦江区等热门留学区县考生,如何筛选优质靠谱的雅思教育机构,掌握实用提分技巧、实现高分目标,是备考首要阻碍。…

2026年安徽民办技校格局:谁在定义“靠谱”的新标准?

一、 核心结论 在安徽省职业教育蓬勃发展的浪潮中,“靠谱”已成为学生与家长择校的核心诉求。它不再局限于传统的就业安置,而是深度融合了高技能培养质量、前瞻性专业布局、多元化升学通道以及坚实的办学底蕴。基于这…

2026雅思网课权威口牌测评排行榜:高分提分方案实用解析推荐

据2026年雅思考试行业权威调研显示,考生在雅思培训选课过程中,常面临优质教育机构筛选难、高分提分技巧匮乏、个性化方案缺失、性价比权衡迷茫等痛点。为帮助考生精准避开误区,本次基于全维度测评体系,结合机构资质…

广州大健康食品OEM工厂推荐:广东诺品健康,一站式营养健康解决方案服务商

广州大健康食品OEM工厂推荐:广东诺品健康,一站式营养健康解决方案服务商 一、为什么选择广州大健康食品OEM工厂? 随着大健康产业的蓬勃发展(2024年国内大健康代加工市场规模突破3800亿元,同比增长22%),越来越多…

2026雅思网上辅导权威靠谱测评排行榜:高分提分机构深度解析

结合2026年雅思考试命题趋势与全国教育机构调研数据,本次针对雅思网上辅导开展全面深度测评,聚焦提分效果、个性化方案、性价比等核心维度,梳理出靠谱实用的机构排行榜。雅思备考中,考生常面临基础薄弱无从下手、单…

CF2106D Flower Boy

https://codeforces.com/problemset/problem/2106/D 解题思路:我们不光要求前缀,还要求后缀。我们可以用前后缀分别来维护a[N]大于b[N]的数目,到时候这届如果前缀数不够的话我们就直接拼起来看行不行。 #include<…

安装ubuntu系统所遇到的各种各样的问题,并附有安装无线网卡,显卡驱动,以及桌面系统教程

1、安装ubuntu系统 这个网上的教程很多,随便找一个教程就好 大致的过程就是,准备u盘,制作u盘(可以用rufus 然后进入bio界面 关闭安全启动,这一步非常关键,没有这一步,接下来的许多问题都无法解决 直接UEFI驱动,…

2026雅思培训在线课程权威靠谱测评排行榜 高分提分方案深度解析

在雅思考试竞争日趋激烈的当下,挑选靠谱的教育机构、获取优质提分技巧、制定个性化实用方案,成为众多考生及家长的核心痛点。市面上雅思培训课程良莠不齐,选课过程中既要兼顾性价比与提分效果,又要甄别机构资质与教…

2026雅思托福培训机构口碑排行榜 权威深度解析测评高分提分方案

随着2026年留学与职场竞争加剧,雅思考试已成为衡量国际语言能力的核心指标,然而考生在雅思培训选课过程中,常面临优质机构筛选难、提分技巧不系统、个性化方案缺失等痛点。如何在海量教育机构中锁定靠谱选择,凭借高…

2026雅思托福培训机构靠谱口碑排行榜 权威深度测评优选方案

基于2026年雅思考试趋势、全国万余名考生反馈及行业师资调研,结合权威教研标准与实战提分数据,我们开展了全面的雅思托福培训机构深度测评,旨在破解考生选课难题。雅思备考中,多数人面临培训机构鱼龙混杂、提分效果…

2026 雅思培训在线课程 靠谱口碑排名榜深度测评推荐解析

随着2026年雅思机考占比持续攀升,评分标准更侧重综合能力,考生在雅思培训选课过程中面临诸多困境:基础薄弱不知如何起步、分数卡在瓶颈难以突破、优质教育机构鱼龙混杂、缺乏个性化提分方案等。多数考生渴望通过权威…

2026 雅思培训在线课程 权威深度测评靠谱口碑排行榜推荐

结合2026年雅思考试趋势与行业调研数据,雅思培训选课市场乱象频发,多数考生面临优质教育机构筛选难、提分技巧匮乏、个性化方案缺失等问题。为助力考生高效突破考试瓶颈、冲刺高分,本文联合教育大数据研究院,通过全…

7.DNS的定义和由来

1、DNS的定义和由来 以上仅供参考,如有疑问,留言联系

2026雅思托福培训机构高分提分权威测评:口碑排行榜解析

基于2026年雅思托福考试改革趋势与行业调研数据,结合万千考生及家长诉求,本次针对市面上主流教育机构开展全面深度测评,聚焦优质课程、高分提分技巧、个性化方案等核心维度,梳理出靠谱实用的机构排行榜。在雅思培训…

2026雅思托福培训机构口碑排行榜 权威深度测评高分提分方案

在雅思培训赛道中,考生常面临选课迷茫、提分乏力、优质教育机构甄别困难等核心痛点,既要兼顾性价比与提分效果,又需寻求权威个性化方案,精准掌握考试技巧实现高分突破。基于2026年雅思考试趋势与全行业深度测评,本…

搜索与回溯算法专题--子集和排列的枚举

枚举排列: 不重集排列: 思路: 用cur表示以cur为当前位置填元素 当cur==n时排列生成完成,输出 然后枚举b数组,尝试在cur上填每一个数 填之前检查一下当前要填的数与a数组之前的数是否相同,不相同代表排列合法,继…

0 基础小白如何快速入门网络安全?这份指南帮你少走弯路

0 基础小白如何快速入门网络安全&#xff1f;这份指南帮你少走弯路 一、为什么要学网络安全&#xff1f; 在互联网时代&#xff0c;网络安全早已不是 “黑客” 的专属领域。从大学生的个人信息保护&#xff0c;到企业的数据安全&#xff0c;甚至国家的网络主权&#xff0c;都离…

【网络安全入门基础教程】网络安全入门终极指南(非常详细)

前言 “1. 结构问题 建议将原文分为三个明确的小标题段落&#xff1a; 破除误解&#xff08;针对黑客形象等问题&#xff09;日常关联性&#xff08;补充更具体的场景&#xff0c;如智能家居漏洞、钓鱼邮件案例&#xff09;学习可行性&#xff08;增加零基础成功案例&#xf…

防劝退指南!网络安全入门全景图:从协议到攻防实战速成

第一章&#xff1a;网络安全的基本概念和术语 网络安全是指保护网络系统、硬件、软件、数据以及用户的隐私和权益&#xff0c;防止其受到未经授权的访问、篡改、窃取或破坏。以下是一些网络安全的基本概念和术语&#xff1a; 漏洞&#xff08;Vulnerability&#xff09;&…