c++领域展开第十八幕——STL(list容器的了解和使用以及手撕模拟)超详细!!!!

c++领域展开第十八幕——STL(list容器的了解和使用以及手撕模拟)超详细!!!!
在这里插入图片描述

文章目录

前言

本专栏上一篇博客把vector的有关实现和细节问题都讲解完毕
今天我们来学习 stl 库的另外一个容器——list
从认识到使用到手撕实现,我来手把手教你
fellow me

一、list的介绍和使用

1.1 list的介绍

list文档

在这里插入图片描述


文档链接在上面啦,大家可以翻译看看
双向循环链表图,list就相当于我们数据结构学习的双向循环链表
只不过在stl库里面给它进行了封装

在这里插入图片描述

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

构造函数((constructor))
list (size_type n, const value_type& val = value_type())——————构造的list中包含n个值为val的元素
list() ————————————构造空的list
list (const list& x) ——————拷贝构造函数
list (InputIterator first, InputIterator last)————用[first, last)区间中的元素构造list

代码展示

 list<int> l1;// 构造空的l1 list<int>l2(4,100);// l2中放4个值为100的元素 list<int>l3(l2.begin(), l2.end());// 用l2的[begin(), end())左闭右开的区间构造l3 list<int>l4(l3);// 用l3拷贝构造l4// 以数组为迭代器区间构造l5int array[]={16,2,77,29}; list<int>l5(array, array +sizeof(array)/sizeof(int));// 列表格式初始化C++11 list<int> l6{1,2,3,4,5};

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

函数声明——————接口说明
begin + end————返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend————返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

代码展示

// 注意这里调用的是list的 begin() const,返回list的const_iterator对象for(list<int>::const_iterator it = l.begin(); it != l.end();++it){ cout <<*it <<" ";// *it = 10; 编译不通过}int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array +sizeof(array)/sizeof(array[0]));// 使用正向迭代器正向list中的元素// list<int>::iterator it = l.begin(); // C++98中语法auto it = l.begin();// C++11之后推荐写法while(it != l.end()){ cout <<*it <<" ";++it;} cout << endl;// 使用反向迭代器逆向打印list中的元素// list<int>::reverse_iterator rit = l.rbegin();auto rit = l.rbegin();while(rit != l.rend()){ cout <<*rit <<" ";++rit;}

1.2.3 list capacity

函数声明————接口说明
empty ——————检测list是否为空,是返回true,否则返回false
size ———————返回list中有效节点的个数

这两个接口直接使用就行,这里就不展示代码了

1.2.4 list element access

函数声明————接口说明
front ——————返回list的第一个节点中值的引用
back ——————返回list的最后一个节点中值的引用
这两个接口也是直接使用就行

1.2.5 list modifiers

函数声明————————接口说明
push_front ———————在list首元素前插入值为val的元素
pop_front ————————删除list中第一个元素
push_back ———————在list尾部插入值为val的元素
pop_back ————————删除list中最后一个元素
insert ——————————在list position 位置中插入值为val的元素
erase ——————————删除list position位置的元素
swap ——————————交换两个list中的元素
clear ——————————清空list中的有效元素
代码展示

int array[]={1,2,3}; list<int>L(array, array +sizeof(array)/sizeof(array[0]));// 在list的尾部插入4,头部插入0 L.push_back(4); L.push_front(0);PrintList(L);// 删除list尾部节点和头部节点 L.pop_back(); L.pop_front();PrintList(L);int array1[]={1,2,3}; list<int>L(array1, array1 +sizeof(array1)/sizeof(array1[0]));// 获取链表中第二个节点auto pos =++L.begin(); cout <<*pos << endl;// 在pos前插入值为4的元素 L.insert(pos,4);PrintList(L);// 在pos前插入5个值为5的元素 L.insert(pos,5,5);PrintList(L);// 在pos前插入[v.begin(), v.end)区间中的元素 vector<int> v{7,8,9}; L.insert(pos, v.begin(), v.end());PrintList(L);// 删除pos位置上的元素 L.erase(pos);PrintList(L);// 删除list中[begin, end)区间中的元素,即删除list中的所有元素 L.erase(L.begin(), L.end());PrintList(L);// 用数组来构造listint array1[]={1,2,3}; list<int>l1(array1, array1 +sizeof(array1)/sizeof(array1[0]));PrintList(l1);// 交换l1和l2中的元素 list<int> l2; l1.swap(l2);PrintList(l1);PrintList(l2);// 将l2中的元素清空 l2.clear(); cout << l2.size()<< endl;

list中还有一些操作,需要用到时大家可参阅list的文档说明

1.2.6 list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针迭代器失效即迭代器所指向的节点的无
,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入
时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭
代器,其他迭代器不会受到影响。

voidTestListIterator1(){int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while(it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值 l.erase(it);++it;}}// 改正voidTestListIterator(){int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while(it != l.end()){ l.erase(it++);// it = l.erase(it);}}

上面有关list的使用和说明就到这里啦,下面我们来模拟实现一下list

二、list的模拟实现

前面在外面的vector的时候就已经模拟实现过,相信大家都有些熟悉了,其实list实现起来也差不多的,都是封装嘛
先用结构体封装一下迭代器

template<classT,classRef,classPtr>structlist_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;// 这里模版的是const迭代器 会返回引用或者取地址其他模版参数 Node* _node;list_iterator(Node* node):_node(node){} Ref operator*(){return _node->_data;} Ptr operator->(){return&_node->_data;} Self&operator++(){ _node = _node->_next;return*this;} Self operator++(int)// 后置++{ Self tmp(*this); _node = _node->_next;return tmp;} Self&operator--(){ _node = _node->_prev;return*this;} Self operator--(int)// 后置--{ Self tmp(*this); _node = _node->_prev;return tmp;}booloperator!=(const Self& it){return _node != it._node;}booloperator==(const Self& it){return _node == it._node;}};

然后就是封装list,实现迭代器,各种增删改查函数,还有默认成员函数
这里我就不像vector一样一个一个赘述啦,相信模拟实现过string和vector之后其实这些写起来也就顺手啦

template<classT>classlist{typedef list_node<T> Node;public:typedef list_iterator<T, T&, T*> iterator;// 多个模版参数 与前面的迭代器封装相对应typedef list_iterator<T,const T&,const T*> const_iterator; iterator begin()// 迭代器{//return iterator(_head->_next); iterator it(_head->_next);return it;} iterator end(){returniterator(_head);} const_iterator begin()const{returnconst_iterator(_head->_next);} const_iterator end()const{returnconst_iterator(_head);}voidempty_init()// 初始化{ _head =new Node; _head->_next = _head; _head->_prev = _head; _size =0;}list()// 默认构造{empty_init();}list(initializer_list<T> lt)// c++11支持 { }构造{empty_init();for(auto& e : lt){push_back(e);}}// lt2(lt1)list(const list<T>& lt)// 拷贝构造{empty_init();for(auto& e : lt){push_back(e);}}// lt1 = lt3 list<T>&operator=(list<T> lt)// 赋值重载{swap(lt);return*this;}voidswap(list<T>& lt)// list内部的 swap{ std::swap(_head, lt._head); std::swap(_size, lt._size);}~list(){clear();delete _head; _head =nullptr;}voidclear()// 清楚函数{ iterator it =begin();while(it !=end()){ it =erase(it);}} size_t size()const{return _size;}voidpush_back(const T& x){/*Node* newnode = new Node(x); Node* tail = _head->_prev; newnode->_prev = tail; tail->_next = newnode; newnode->_next = _head; _head->_prev = newnode;*/insert(end(), x);}voidpush_front(const T& x){insert(begin(), x);}voidpop_front(){erase(begin());}voidpop_back(){erase(--end());}voidinsert(iterator pos,const T& x){ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(x); prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode;++_size;} iterator erase(iterator pos){assert(pos !=end()); Node* cur = pos._node; Node* nextNode = cur->_next; Node* prevNode = cur->_prev; prevNode->_next = nextNode; nextNode->_prev = prevNode;delete cur;--_size;returniterator(nextNode);}private: Node* _head; size_t _size;};

经过几次的模拟实现stl,发现stl的容器大多都是类似的,有点期待后面的map和set
list 的模拟实现就到这里啦

list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

在这里插入图片描述

总结

总结

C++ STL中的list是基于双向循环链表实现的序列容器,支持高效插入删除(O(1)时间复杂度),但随机访问效率较低。其核心特性包括:通过迭代器遍历元素(支持正向/反向迭代器)、插入操作不引发迭代器失效(删除仅影响被删节点迭代器)。模拟实现需封装节点结构体,设计泛型迭代器(重载++/--/*等操作),并实现深拷贝控制。与vector对比,list适合频繁增删场景,而vector更适合随机访问和内存连续需求。理解其底层实现有助于优化数据操作逻辑。
不要走开,小编持续更新中~~~~~
在这里插入图片描述

Read more

Python从0到100完整学习指南(必看导航)

Python 从 0 到 100 完整学习路线(2025–2026 实用版) 这是一条目前在中文社区被验证最多次、性价比最高、就业/副业/考研/转行都适用的 Python 学习路径。 分为 8 个大阶段,每个阶段给出: * 核心目标 * 推荐学习时长(每天 2–4 小时估算) * 最值得学的资源(2025–2026 仍活跃且评价最高的) * 必须掌握的技能清单 * 阶段性小目标 / 实战项目建议 阶段划分总览表 阶段名称目标人群建议时长累计总时长核心关键词0准备期完全零基础3–7 天1 周环境、IDE、学习心态1Python 基础语法零基础 → 能写小工具3–6 周1–2 个月变量、循环、函数、类2Pythonic

Python版本管理:让pyenv成为你的版本调停者

Python版本管理:让pyenv成为你的版本调停者 【免费下载链接】pyenvSimple Python version management 项目地址: https://gitcode.com/GitHub_Trending/py/pyenv 在Python开发中,多版本共存与开发环境隔离是开发者常面临的挑战。当你需要同时维护基于Python 2.7的遗留系统和Python 3.10的新项目时,当你在教学中需要向学生展示不同版本间的语法差异时,当你需要确保代码在多个Python版本下都能正常运行时,一个可靠的版本管理工具就显得尤为重要。pyenv正是这样一款工具,它能够让你在不同Python版本间无痛切换,为你的开发工作提供稳定而灵活的环境支持。 当Python版本打架时:问题与核心价值 想象一下这样的场景:你正在开发一个新项目,需要使用Python 3.9的新特性,然而你的系统默认Python版本是2.7,直接升级系统Python可能会导致其他依赖旧版本的应用无法正常工作。或者,你接手了一个旧项目,它依赖于特定版本的Python和库,而你本地的开发环境与此不符。这些问题

快速上手:在 Python 环境中安装与配置 Gurobi

快速上手:在 Python 环境中安装与配置 Gurobi

快速上手:在 Python 环境中安装与配置 Gurobi 一、Gurobi简介 Gurobi 是由美国 Gurobi Optimization 公司开发的一款高性能商业数学优化求解器,广泛应用于学术研究与工业领域。它能够高效求解以下类型的优化问题: * 线性规划(LP) * 整数规划(IP) * 混合整数规划(MIP) * 二次规划(QP) * 二次约束规划(QCP) * 非线性规划(部分支持,如含对数、指数、三角函数、分段函数等) 主要特点: * 求解速度快、精度高:在多项第三方评测中性能领先,曾于2010年超越 CPLEX 成为行业标杆。 * 多语言支持:提供 Python、C/C++、Java、.NET、MATLAB、R 等接口,其中 Python 接口(