C++ 基础数据结构与 STL 容器详解

C++ 基础数据结构与 STL 容器详解

C++ 的基础数据结构和 STL 容器是编程的核心基础,不同容器因底层实现不同,适用场景也天差地别。下面我会按「基础数据结构(语言内置)」和「STL 容器(标准库)」两类,清晰讲解它们的底层实现和核心特点,内容兼顾新手易懂性和实用性。

一、C++ 基础数据结构(语言内置)

这类是 C++ 原生支持的基础类型/结构,无复杂封装,是 STL 容器的底层基石。

数据结构 底层实现原理 核心特点
数组(array) 连续的内存空间,元素按索引顺序存储,编译期确定大小(栈/静态区分配)。 随机访问 O(1),增删 O(n),大小不可变
指针 本质是存储内存地址的变量,通过地址间接访问数据。 灵活操作内存,需手动管理,易出越界/野指针问题
结构体(struct) 把不同类型数据打包在连续内存中(内存对齐),无额外封装。 自定义复合类型,仅数据聚合,无成员函数(可手动加)
示例:原生数组的底层体现
C++ 复制代码
#include <iostream>
using namespace std;

int main() {
    // 连续内存存储:地址依次递增(int 占4字节,地址差4)
    int arr[3] = {10, 20, 30};
    cout << "arr[0] 地址:" << &arr[0] << endl;
    cout << "arr[1] 地址:" << &arr[1] << endl;
    cout << "arr[2] 地址:" << &arr[2] << endl;
    
    // 随机访问:通过 基地址 + 索引*类型大小 直接定位(O(1))
    cout << arr[1] << endl; // 等价于 *(arr + 1)
    return 0;
}

二、C++ STL 容器(标准库封装)

STL 容器是对基础数据结构的封装,分为「序列式容器」「关联式容器」「适配器容器」三类,底层实现各有侧重:

1. 序列式容器(按插入顺序存储,元素有序)
容器 底层实现原理 核心特点 适用场景
vector 动态数组,连续内存 + 预分配「容量(capacity)」,满了后扩容(通常2倍),重新分配内存并拷贝元素。 随机访问 O(1),尾部增删 O(1),中间增删 O(n) 频繁读、尾部增删,如数据遍历/统计
list 双向循环链表,每个节点包含数据、前驱指针、后继指针,非连续内存。 随机访问 O(n),任意位置增删 O(1),无内存浪费 频繁插入/删除,如链表操作、任务队列
forward_list 单向链表,仅含后继指针,比 list 更节省内存。 仅单向遍历,任意位置增删 O(1),无 size() 方法 内存受限的单向遍历场景
deque 分段连续内存(数组 + 缓冲区),底层用「中控数组」管理多个连续缓冲区,首尾各有一个指针。 随机访问 O(1),首尾增删 O(1),中间增删 O(n) 双端队列、滑动窗口,如队列/栈底层
array 封装的静态数组,连续内存,编译期确定大小(栈分配),比原生数组更安全(有边界检查)。 同原生数组,不可扩容,支持 STL 算法 替代原生数组,需固定大小场景
2. 关联式容器(按键排序,元素无序)

底层依赖「红黑树」或「哈希表」,核心是快速查找:

容器 底层实现原理 核心特点 适用场景
set/multiset 红黑树(自平衡二叉搜索树),set 键唯一,multiset 键可重复,元素自动排序。 查找/插入/删除 O(logn),有序,无随机访问 有序去重、范围查找,如字典、排序集合
map/multimap 红黑树,map 键值对(键唯一),multimap 键可重复,按键排序。 set,通过键访问值 键值映射,如配置表、缓存
unordered_set/unordered_multiset 哈希表(开链法解决哈希冲突),无自动排序,键唯一/可重复。 平均查找/插入/删除 O(1),无序,最坏 O(n) 高频查找、无需排序,如去重、存在性判断
unordered_map/unordered_multimap 哈希表,键值对,无自动排序,键唯一/可重复。 unordered_set,通过键快速查值 高频键值查找,如缓存、字典
3. 适配器容器(封装其他容器,改变接口)

无独立底层,依赖上述容器适配:

容器 底层依赖(默认) 核心特点 适用场景
stack deque 后进先出(LIFO),仅支持首尾操作 栈操作,如表达式求值、回溯
queue deque 先进先出(FIFO),仅支持首尾操作 队列操作,如任务排队、消息队列
priority_queue vector + 堆(默认大顶堆) 优先级最高的元素先出,底层堆排序 优先队列,如任务调度、贪心算法

关键补充:核心底层原理拆解

1. 红黑树(set/map 底层)

红黑树是「自平衡二叉搜索树」,通过 5 条规则保证平衡(节点红/黑、根黑、叶节点黑、红节点子节点黑、任意节点到叶节点的黑节点数相同),避免二叉搜索树退化成链表,保证操作复杂度稳定在 O(logn)。

2. 哈希表(unordered_* 底层)

哈希表通过「哈希函数」将键映射到数组下标,实现 O(1) 访问;若哈希冲突(不同键映射到同一下标),STL 用「开链法」:每个下标对应一个链表,冲突元素存入链表,最坏情况下链表过长会导致操作复杂度 O(n)(可通过扩容缓解)。

3. vector 扩容机制

vector 初始容量通常为 0/1,满了后扩容为原容量的 1.5~2 倍(不同编译器不同),步骤:

  1. 分配新的更大内存;

  2. 拷贝原元素到新内存;

  3. 释放原内存;

  4. 指向新内存。

⚠️ 扩容会导致迭代器失效,建议提前用 reserve() 预分配容量。

总结

  1. 连续内存容器array/vector/deque):随机访问快,增删慢,适合读多写少场景;

  2. 链表容器list/forward_list):增删快,访问慢,适合写多读少场景;

  3. 有序关联容器set/map):红黑树实现,有序+稳定 O(logn) 操作;无序关联容器(unordered_*):哈希表实现,无序+平均 O(1) 操作,是高频查找首选;

  4. 适配器容器stack/queue):无独立底层,仅封装接口,按需选择底层容器即可。

核心选择原则:优先根据「访问/增删频率」和「是否有序」选择容器,比如高频查找选 unordered_map,有序查找选 map,尾部增删选 vector

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

相关推荐
morrisonwu2 小时前
kafka4.2对应php rdkafka扩展安装以及php的producer和consumer写法及避坑
开发语言·php
Lyyaoo.2 小时前
【JAVA基础面经】== 和 equals() 的区别
java·开发语言·jvm
报错小能手2 小时前
ios开发方向——swift并发进阶核心 async/await 详解
开发语言·ios·swift
青花瓷2 小时前
采用QT下MingW编译opencv4.8.1
开发语言·qt
旖-旎2 小时前
链表(两两交换链表中的节点)(2)
数据结构·c++·学习·算法·链表·力控
赫瑞2 小时前
Java中的日期类
java·开发语言
吕司2 小时前
Linux线程同步
linux·服务器·开发语言
神の愛2 小时前
java日志功能
java·开发语言·前端
Reuuse2 小时前
基于 C++ 的网页五子棋对战项目实战
开发语言·c++