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

STL源码解析之list(1)

与vector相比,list 的每个元素是一个独立的节点,包含前驱和后继指针。节点可在内存中任意位置,通过指针链接。

一、list与vector对比

操作vectorlist
随机访问([i]O(1) – 极快O(n) – 需要遍历
头部插入/删除O(n) – 移动所有后续元素O(1) – 只改几个指针
尾部插入/删除O(1) – 摊销常数(可能触发扩容)O(1) – 双向链表尾节点
中间插入/删除O(n) – 移动插入点后的元素O(1) – 前提是已找到位置(迭代器)
查找给定值O(n) – 线性搜索O(n) – 线性搜索
排序可用std::sort(O(n log n))需用成员sort()(O(n log n))
内存开销很低(仅存储元素,可能少量预留空间)高(每个节点额外存储两个指针)
缓存友好性极好(连续内存,预取)差(节点分散,缓存缺失多)
  • vector插入元素可能引起重新分配,使所有迭代器、引用、指针失效。删除元素后,被删元素之后的迭代器失效。

  • list插入或删除节点,仅影响指向被操作节点的迭代器,其他迭代器始终有效。这是链表的重要优势。

二、源码解析

1)list节点定义

template <class T> struct __list_node { typedef void* void_pointer; void_pointer next; void_pointer prev; T data;

STL list是一个双向链表,next指向下一个节点,prev指向前一个节点

2)前向遍历与后向遍历

self& operator++() { node = (link_type)((*node).next); return *this; } self operator++(int) { self tmp = *this; ++*this; return tmp; } self& operator--() { node = (link_type)((*node).prev); return *this; } self operator--(int) { self tmp = *this; --*this; return tmp; }

std::list<int> lst = {1, 2, 3, 4, 5}; for (auto it = lst.begin(); it != lst.end(); ++it) { std::cout << *it << " "; } // 输出: 1 2 3 4 5 for (auto it = lst.rbegin(); it != lst.rend(); ++it) { std::cout << *it << " "; } // 输出: 5 4 3 2 1

3)插入操作

在postion处插入元素

iterator insert(iterator position, const T& x) { link_type tmp = create_node(x); // 調整雙向指標,使 tmp 安插進去。 tmp->next = position.node; tmp->prev = position.node->prev; (link_type(position.node->prev))->next = tmp; position.node->prev = tmp; return tmp; }

在链表头部插入一个元素

void push_front(const T& x) { insert(begin(), x); }

在链表尾部插入一个元素

void push_back(const T& x) { insert(end(), x); }

4)删除操作

删除position处node

erase返回:指向被删元素之后的下一个有效元素的迭代器(若已无后续,返回end()

iterator erase(iterator position) { link_type next_node = link_type(position.node->next); link_type prev_node = link_type(position.node->prev); prev_node->next = next_node; next_node->prev = prev_node; destroy_node(position.node); return iterator(next_node); }

删除链表首部

void pop_front() { erase(begin()); }

删除链表尾部

void pop_back() { iterator tmp = end(); erase(--tmp); }

list插入/删除只修改指针,从不拷贝或移动元素本身。对比vector:当元素类型拷贝/移动成本高时(如包含大量数据的结构),vector的重新分配或插入/删除操作会付出高昂的元素移动代价。

list本身也有局限性,对比vector的主要代价

  • 内存开销大:每个元素多存两个指针(prev/next),对于小对象内存浪费严重。

  • 缓存不友好:节点在堆中分散,遍历时缓存命中率低,速度远慢于vector

  • 不支持随机访问:获取第 N 个元素需要 O(n) 遍历。

经验法则:默认使用vector,除非你明确需要list的上述优点,并且vector的缺点(中间插入/删除 O(n)、迭代器失效、大块连续内存)成为实际瓶颈。

http://www.rkmt.cn/news/1497430.html

相关文章:

  • OEXN:“太空上市预期持续升温”
  • 从RTL代码到GDSII流片:一个真实小模块的Synopsys工具链实战踩坑记录
  • 别再只背公式了!用‘小学生也能懂’的比喻,彻底搞懂RSA低加密指数攻击为什么危险
  • 从热水器到充电桩:手把手教你根据电器功率算清空开型号(C32/C40/Dxx详解)
  • 03-状态管理与路由——05-React Router 基础配置
  • 别再被虚线框困扰了!手把手教你用Visio+pdfcrop+Acrobat DC搞定LaTeX插图阴影问题
  • 纯文科能报大数据本科吗?四条迂回路径+CDA破局
  • Moneta Markets亿汇:“比特币反弹走势仍脆弱”
  • 告别臃肿!VS2022只装C++桌面开发,如何精准搭配Qt 5.12打造轻量级GUI编程环境
  • 告别Apex!用PyTorch Lightning轻松搞定半精度训练与多卡同步(保姆级避坑指南)
  • 2026年6月丰宁坝上草原住宿民宿甄选指南:短途自驾、朋友聚会、观景食宿一站式参考 - 海棠依旧大
  • 保姆级教程:用MounRiver Studio和WCH-Link点亮你的第一个CH32V103C开发板
  • 三明百达翡丽+宝珀手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 告别IP依赖:在Vivado中直接手写MMCME2_ADV原语生成多路时钟(附参数计算避坑指南)
  • 遗传算法实战调参指南:从早熟收敛到工程落地
  • INA219采样不准?从硬件选型到软件校准的避坑指南
  • 三亚百达翡丽+宝珀手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 嵌入式设备如何用C语言对接天翼物联网平台CTWing?手把手教你移植SDK到MCU
  • 从“数独思维”到“启发式搜索”:我是如何用六条策略搞定日历拼图这个烧脑游戏的
  • 工业级遗传算法实战:调参、防早熟与收敛诊断
  • Mac玩转51单片机:除了Keil,用开源工具链(sdcc/stcgal)开发是种什么体验?
  • STM32F103的RTC掉电不保存?手把手教你修改RT-Thread的drv_rtc.c源码
  • 手把手教你用SuperMap iClient3D for WebGL加载山东省天地图(附完整代码与参数详解)
  • 阜阳帝舵+浪琴手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 娄底卡地亚+GP芝柏表手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 2026免费PDF转图片工具教程:在线、电脑软件、小程序全攻略 - 办公小帮手
  • Vue 3 + Tailwind CSS 实战:如何快速封装一套可复用的Hover动画组件库
  • LLM生成参考文献的检测:语义指纹与GNN技术
  • 甘南法穆兰+宝玑手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 告别乱糟糟的SQL!手把手教你配置DataGrip的专属格式化模板(附保姆级参数详解)