C++ : 手写简化版 list 完整实现与高频面试题
一、手写简化版 list 完整实现list的核心架构双向循环链表 哨兵节点 双向迭代器覆盖双端增、删、任意位置插入、删除、链表拼接等核心能力。#includeiostream#includecassert#includeutility#includeiterator// 链表节点结构体前驱指针 后继指针 数据templatetypenameTstructListNode{ListNode*prev;ListNode*next;T data;// 支持原地构造元素templatetypename...ArgsexplicitListNode(Args...args):prev(nullptr),next(nullptr),data(std::forwardArgs(args)...){}};// 双向迭代器封装节点指针实现迭代器语义templatetypenameTclassListIterator{public:// 迭代器类型定义符合 STL 规范usingiterator_categorystd::bidirectional_iterator_tag;usingvalue_typeT;usingdifference_typeptrdiff_t;usingpointerT*;usingreferenceT;ListNodeT*node;explicitListIterator(ListNodeT*n):node(n){}// 解引用与成员访问referenceoperator*()const{returnnode-data;}pointeroperator-()const{return(node-data);}// 前置自增ListIteratoroperator(){nodenode-next;return*this;}// 后置自增ListIteratoroperator(int){ListIterator tmp*this;*this;returntmp;}// 前置自减ListIteratoroperator--(){nodenode-prev;return*this;}// 后置自减ListIteratoroperator--(int){ListIterator tmp*this;--*this;returntmp;}// 相等性比较booloperator(constListIteratorother)const{returnnodeother.node;}booloperator!(constListIteratorother)const{returnnode!other.node;}};// 简化版 list 容器templatetypenameTclassMyList{private:ListNodeT*sentinel;// 哨兵节点哑节点不存有效数据size_t _size;// 元素总数保证 O(1) 求大小public:usingiteratorListIteratorT;// 默认构造初始化哨兵形成自环MyList():_size(0){sentinelnewListNodeT();sentinel-prevsentinel;sentinel-nextsentinel;}// 析构释放所有节点 哨兵~MyList(){clear();deletesentinel;sentinelnullptr;}// 清空所有元素恢复空链表状态voidclear(){ListNodeT*cursentinel-next;while(cur!sentinel){ListNodeT*tmpcur;curcur-next;deletetmp;}sentinel-prevsentinel;sentinel-nextsentinel;_size0;}// 迭代器接口 iteratorbegin(){returniterator(sentinel-next);}iteratorend(){returniterator(sentinel);}// 容量接口 boolempty()const{return_size0;}size_tsize()const{return_size;}// 元素访问 Tfront(){assert(!empty()front called on empty list);returnsentinel-next-data;}Tback(){assert(!empty()back called on empty list);returnsentinel-prev-data;}// 核心插入操作 // 在 pos 位置之前插入元素返回指向新元素的迭代器iteratorinsert(iterator pos,constTvalue){ListNodeT*new_nodenewListNodeT(value);ListNodeT*curpos.node;ListNodeT*prev_nodecur-prev;// 四步指针操作完成插入头尾位置逻辑完全一致new_node-prevprev_node;new_node-nextcur;prev_node-nextnew_node;cur-prevnew_node;_size;returniterator(new_node);}voidpush_back(constTvalue){insert(end(),value);}voidpush_front(constTvalue){insert(begin(),value);}// 核心删除操作 // 删除 pos 位置的元素返回指向被删元素下一个的迭代器iteratorerase(iterator pos){assert(pos!end()erase called on end iterator);ListNodeT*curpos.node;ListNodeT*prev_nodecur-prev;ListNodeT*next_nodecur-next;// 前后节点直接互连跳过被删节点prev_node-nextnext_node;next_node-prevprev_node;deletecur;--_size;returniterator(next_node);}voidpop_back(){assert(!empty()pop_back called on empty list);erase(--end());}voidpop_front(){assert(!empty()pop_front called on empty list);erase(begin());}// 链表专属splice 拼接 // 将 other 整个链表的节点移动到 pos 位置之前other 变为空// 零拷贝、零构造析构纯指针操作voidsplice(iterator pos,MyListother){if(other.empty())return;if(thisother)return;// 自身拼接无意义ListNodeT*other_headother.sentinel-next;ListNodeT*other_tailother.sentinel-prev;ListNodeT*pos_nodepos.node;ListNodeT*pos_prevpos_node-prev;// 断开 other 的哨兵恢复空链表状态other.sentinel-nextother.sentinel;other.sentinel-prevother.sentinel;// 将 other 的节点接入当前链表pos_prev-nextother_head;other_head-prevpos_prev;other_tail-nextpos_node;pos_node-prevother_tail;// 更新计数_sizeother._size;other._size0;}// 链表的翻转voidreverse()noexcept{// reverse sequence// 空链表或者仅有一个元素无需反转if(_size1)return;ListNodeT*cursentinel-next;while(cur!sentinel){ListNodeT*next_nodecur-next;// 临时存储下一个元素std::swap(cur-prev,cur-next);// 交换当前元素的前后指针指向curnext_node;// 向下一个元素偏移}//交换哨兵元素的前后指针std::swap(sentinel-next,sentinel-prev);}};// 测试用例 intmain(){MyListintlst;// 测试双端插入for(inti1;i5;i)lst.push_back(i);// 尾插 1 2 3 4 5for(inti0;i-2;--i)lst.push_front(i);// 头插 -2 -1 0std::cout双端插入后元素总数lst.size()\n;std::cout顺序遍历;for(autoitlst.begin();it!lst.end();it){std::cout*it ;}std::cout\n\n;// 测试中间插入与删除autoitlst.begin();it;it;// 指向第3个元素lst.insert(it,100);// 在第3个位置前插入100std::cout中间插入100后;for(intx:lst)std::coutx ;std::cout\n;lst.erase(--lst.end());// 删除最后一个元素std::cout删除尾元素后;for(intx:lst)std::coutx ;std::cout\n\n;// 测试 splice 拼接MyListintother;other.push_back(10);other.push_back(20);other.push_back(30);lst.splice(lst.begin(),other);std::coutsplice拼接后当前list;for(intx:lst)std::coutx ;std::cout\n拼接后other是否为空std::boolalphaother.empty()\n;lst.reverse();std::coutreverse翻转后当前list:;for(intx:lst)std::coutx ;std::coutstd::endl;return0;}说明本实现为教学简化版与标准库的差异主要在于未使用分配器、未实现 const 迭代器与异常安全核心指针操作、哨兵设计、迭代器逻辑与 STL 源码完全对齐。二、list 高频面试题与标准答案基础概念类STL list 的底层数据结构是什么答底层是双向循环链表并通过哨兵节点哑节点统一边界处理。每个节点包含前驱指针、后继指针和数据元素容器本身只持有哨兵节点指针哨兵的 next 指向头节点、prev 指向尾节点首尾相连形成闭环。list 属于什么迭代器类型支持哪些操作答双向迭代器bidirectional_iterator仅支持前后逐个移动、--不支持随机跳跃、-、下标访问因此无法直接使用std::sort等要求随机访问迭代器的算法。list 是顺序容器吗和 vector、deque 的本质区别是什么答list 是顺序容器元素按插入顺序线性排列。本质区别在于内存布局vector整块连续内存随机访问极快中间插删慢deque分段连续内存双端插删快支持随机访问但常数开销大list离散的链表节点不支持随机访问已知迭代器时任意位置插删 O(1)底层原理类为什么 list 要使用哨兵节点有什么好处答哨兵节点是不存储有效数据的虚拟节点始终存在并首尾相连形成闭环。核心好处消除边界特判头插、尾插、中间插入的指针操作逻辑完全一致不需要单独处理空链表、头节点、尾节点等边界场景简化迭代器语义end()直接指向哨兵天然匹配“最后一个元素的下一个位置”减少代码分支降低出错概率list 的size()是 O(1) 还是 O(n)答C11 标准强制要求所有容器的size()为 O(1)因此主流实现GCC libstdc 等都会在 list 中维护一个计数成员变量插入删除时同步更新。C11 之前的旧实现没有 size 成员调用size()需要遍历整个链表计数时间复杂度 O(n)。详细说明 list 的迭代器失效规则答list 是所有 STL 容器中迭代器稳定性最强的插入操作所有已有迭代器、引用、指针全部保持有效不受任何影响删除操作仅被删除节点对应的迭代器、引用、指针失效其余节点全部不受影响根本原因是链表增删只修改节点指针不移动已有节点的内存地址。list 的splice操作是什么时间复杂度是多少答splice是 list 独有的链表拼接能力可以将另一个 list 的单个节点、区间或整个链表直接移动到当前 list 的指定位置。全程纯指针操作不拷贝元素零构造析构开销。时间复杂度整个链表转移、单个节点转移O(1)区间转移O(k)k 为区间元素个数用于统计数量更新 size 成员C11 之前没有 size 成员所有 splice 均为 O(1)。为什么 list 要自己实现sort、merge、unique等成员函数答分为两类原因通用算法无法使用std::sort要求随机访问迭代器list 只有双向迭代器不满足要求通用算法效率更低std::merge、std::remove等基于“拷贝/赋值元素”实现而 list 可以直接移动节点指针零拷贝性能更高因此 list 专门实现了成员版本充分发挥链表结构的优势。list::sort的底层实现是什么答主流 STL 实现采用迭代版自底向上归并排序空间复杂度 O(1)仅维护固定大小的指针数组不额外分配节点时间复杂度 O(n log n)是稳定排序。它维护一个指针数组每个位置对应一条长度为 2^i 的有序子链表逐个将节点合并进对应层级最终拼接出完整有序链表。对比与选型类工程上为什么默认优先用 vector 而不是 list答核心原因是缓存友好性的巨大差距vector 的连续内存可以被 CPU 缓存预取遍历命中率极高访问速度快list 的节点散落在堆内存中地址不连续遍历时频繁触发缓存未命中访问主存的耗时是缓存的几十上百倍大多数场景下“找到插入位置的遍历开销”已经抵消了链表插删 O(1) 的理论优势甚至更慢。因此工程上默认优先使用 vector实测证明 list 更优时再切换。什么场景下适合使用 list答满足以下条件时可考虑 list频繁在中间位置插入删除且能快速获取插入位置的迭代器如配合哈希表做索引需要保证元素地址/引用长期稳定不能因增删失效需要频繁进行节点拼接、转移如 LRU 缓存实现元素体积大、拷贝成本极高插删移动代价远大于遍历开销list 和 forward_list 有什么区别答forward_list是 C11 引入的单链表与 list 的核心差异list 是双向链表支持双向迭代头尾增删均为 O(1)forward_list 是单向链表仅支持正向遍历没有size()成员保证零额外开销只支持头插和指定位置之后的插入forward_list 更轻量内存开销更小但功能受限适合极致追求空间、仅需单向操作的场景细节与坑点类空链表的begin()和end()是什么关系答二者相等。空链表中哨兵节点的prev和next都指向自己因此begin()指向哨兵的 next即哨兵自身end()也指向哨兵完全符合 STL 迭代器语义。对 list 执行sort后迭代器会失效吗答不会失效。list::sort通过修改节点指针重排链表不移动、不拷贝元素本身所有节点的内存地址不变因此迭代器、引用、指针全部保持有效仅元素顺序发生变化。list 每个节点的额外内存开销是多少答64 位系统下每个节点包含两个指针prev next共 16 字节的额外开销再加上数据本身的大小。若存储int4 字节额外开销高达 300%内存利用率极低若存储大对象如几百字节的结构体指针开销占比可忽略。

相关新闻

分子互作研究:解锁生命活动的底层分子密码

分子互作研究:解锁生命活动的底层分子密码

一、什么是分子互作?分子相互作用(分子互作)泛指生物大分子之间、大分子与小分子之间发生可逆结合行为,包含蛋白 - 蛋白、蛋白 - 多肽、蛋白 - 核酸、蛋白 - 小分子、抗原 - 抗体等结合反应。 生命体绝大多数生理过程依靠分子互作…

2026/9/21 3:34:45 阅读更多 →
负反馈电路稳定性分析:从波特图到实战设计陷阱

负反馈电路稳定性分析:从波特图到实战设计陷阱

1. 项目概述:重新审视负反馈的稳定性分析在电子电路设计,尤其是模拟电路和运算放大器应用领域,负反馈是一个既基础又核心的概念。它被用来改善增益精度、扩展带宽、降低非线性失真,并提高输入阻抗或降低输出阻抗。然而&#xff0c…

2026/9/23 14:46:53 阅读更多 →
C++ : list 源码级深度拆解——双向循环链表的优雅与代价

C++ : list 源码级深度拆解——双向循环链表的优雅与代价

在 STL 顺序容器三巨头中,list 是最“纯粹”的链式结构代表:vector 主打连续内存的极致访问效率,deque 主打双端操作的折中平衡,list 则把任意位置插删的能力拉满,代价是彻底放弃随机访问与缓存友好性。 它的底层是经典…

2026/9/22 7:32:58 阅读更多 →

最新新闻

3步搞定previouspage:从入门到精通避坑指南

3步搞定previouspage:从入门到精通避坑指南

3步搞定previouspage:从入门到精通避坑指南 看了一堆教程还是不会写项目?别慌,这很正常。 很多开发者卡在分页逻辑上,特别是处理“上一页”跳转时,边界条件没处理好,测试一跑就报错。 今天带你用实战代码,把…

2026/9/23 14:47:08 阅读更多 →
Apache Flink DataStream 连接器全景指南:预定义源汇、官方连接器与接入方式详解

Apache Flink DataStream 连接器全景指南:预定义源汇、官方连接器与接入方式详解

Apache Flink DataStream 连接器全景指南:预定义源汇、官方连接器与接入方式详解 【免费下载链接】flink 项目地址: https://gitcode.com/gh_mirrors/fli/flink 导读 本文以 Apache Flink DataStream API 的连接器体系为核心,系统梳理 Flink 提…

2026/9/23 14:47:07 阅读更多 →
TensorBoard 框架集成指南:PyTorch / TensorFlow / Lightning / Transformers 等多框架训练可视化实战

TensorBoard 框架集成指南:PyTorch / TensorFlow / Lightning / Transformers 等多框架训练可视化实战

AI 技能人工智能大模型深度学习 【免费下载链接】AI-Research-SKILLs Comprehensive open-source library of AI research and engineering skills for any AI model. Package the skills and your claude code/codex/gemini agent will be an AI research agent with full hor…

2026/9/23 14:47:07 阅读更多 →
Kornia YUV422 转换中的色度平面形状校验:`yuv422_to_rgb` 如何用 `ShapeError` 取代裸 `RuntimeError`

Kornia YUV422 转换中的色度平面形状校验:`yuv422_to_rgb` 如何用 `ShapeError` 取代裸 `RuntimeError`

计算机视觉人工智能深度学习图像处理 【免费下载链接】kornia 🐍 Geometric Computer Vision Library for Spatial AI 项目地址: https://gitcode.com/gh_mirrors/ko/kornia 点击查看 免费下载 导读 本文围绕 Kornia 颜色空间转换模块中的一个具体修复…

2026/9/23 14:47:07 阅读更多 →
医学图像分割最小可行系统:U-Net/DeepLabV3+/nnUNet即插即用

医学图像分割最小可行系统:U-Net/DeepLabV3+/nnUNet即插即用

简介:本资源是一份面向计算机专业本科生的毕业设计或课程作业实践包,聚焦基于深度学习的医学图像语义分割任务,适用于AI医疗方向入门到进阶的学习与复现。项目融合深度学习建模、Python端训练部署与C端高性能推理优化,覆盖数据预处…

2026/9/23 14:47:07 阅读更多 →
EOSIO 代币转账实战:使用 cleos transfer 转移 eosio.token 合约代币

EOSIO 代币转账实战:使用 cleos transfer 转移 eosio.token 合约代币

区块链 【免费下载链接】eos An open source smart contract platform 项目地址: https://gitcode.com/gh_mirrors/eo/eos 点击查看 免费下载 本指南以 EOSIO 开源智能合约平台(本仓库 eo/eos)中 cleos 客户端的 transfer 子命令为核心&…

2026/9/23 14:46:06 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →