2.6、list的底层原理和常用函数
底层数据结构
- • 双向循环链表实现:
std::list
底层采用双向循环链表,每个节点包含三个部分:- • 数据元素(存储用户数据);
- • 前驱指针(prev):指向前一个节点;
- • 后继指针(next):指向后一个节点。
- • 内存分布特性:
节点在内存中非连续分布,通过指针形成逻辑上的序列,插入/删除操作仅需修改指针指向,无需移动元素,时间复杂度为 O(1)(已定位节点时)。
迭代器设计与特性
- • 迭代器本质:
迭代器内部封装了一个指向链表节点的指针,通过重载以下运算符实现链表遍历:- •
*
:解引用获取节点数据; - •
++
:移动到后继节点(operator++()
); - •
--
:移动到前驱节点(operator--()
); - •
==
/!=
:判断迭代器是否指向同一节点。
- •
- • 关键限制:
- • 不支持随机访问(无法通过下标
operator[]
或跳跃式操作直接定位元素); - • 遍历效率:访问第
n
个元素需顺序移动n
次迭代器,时间复杂度为 O(n)。
- • 不支持随机访问(无法通过下标
核心优势与适用场景
优势:
- 1. 高效的插入/删除操作:
- • 任意位置插入/删除只需修改指针,无需移动后续元素,尤其适合频繁在序列中间修改数据的场景(如任务队列动态增删任务)。
- 2. 迭代器稳定性:
- • 插入/删除操作仅使当前节点的迭代器失效,其他迭代器保持有效(vector扩容时所有迭代器失效)。
适用场景:
- • 高频中间操作:如链表式数据结构(undo/redo系统、多项式表达式动态修改);
- • 无需随机访问:仅需顺序遍历的场景(如日志流式处理);
- • 元素大小不确定:避免连续容器(vector/deque)的扩容开销。
常用操作的底层实现
操作 | 实现逻辑 |
push_back |
直接修改尾节点的next 指针指向新节点,并更新链表尾指针,时间复杂度 O(1)。 |
push_front |
修改头节点的prev 指针指向新节点,并更新链表头指针,时间复杂度 O(1)。 |
erase(iter) |
通过迭代器定位节点,修改其前驱节点的next 指针和后继节点的prev 指针,删除节点,时间复杂度 O(1)。 |
sort() |
基于链表的归并排序实现,时间复杂度 O(n log n)(避免vector的空间拷贝开销)。 |
unique() |
遍历链表,比较相邻节点数据,删除重复节点,时间复杂度 O(n)。 |
总结
std::list
的设计核心是以双向链表为基础,通过迭代器封装指针操作,换取高效的动态修改能力。其核心特点可概括为:
- • 数据结构:双向循环链表,内存非连续,节点通过指针连接;
- • 操作效率:插入/删除 O(1)(已定位),访问 O(n);
- • 适用边界:适合“高频中间修改+低频随机访问”场景,如事件队列、动态链表算法。
本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
(加入我的知识星球,免费获取账号,解锁所有文章。)
阅读剩余
版权声明:
作者:讳疾忌医-note
链接:https://www.1217zy.vip/archives/1814
文章版权归作者所有,未经允许请勿转载。
THE END