list 实现链表封装节点的底层逻辑:如何克服不连续无法正常访问挑战 - 详解

news/2025/10/19 15:47:41/文章来源:https://www.cnblogs.com/wzzkaifa/p/19150928

在这里插入图片描述

胖咕噜的稞达鸭:个人主页

个人专栏: 《数据结构》《C++初阶高阶》

⛺️技术的杠杆,撬动整个世界!

在这里插入图片描述

list 功能实现

关于迭代器的申明:
功能:iterator/reverse_iterator/const_iterator/const_reverse_iterator
性质:
单向:forward_list/unordered_map… 只能迭代器++
双向:list/map/set… 迭代器++/–
随机:vector/string/deque… 迭代器++/–/+/-

  1. list的遍历
#include<iostream>#include<list>#include<algorithm>using namespace std;void test_list1(){list<int>It;It.push_back(1);It.push_back(2);It.push_back(3);It.push_back(4);It.push_back(5);list<int>::iterator it = It.begin();while(it!=It.end()){cout << *it << " ";++it;}cout << endl;//能用迭代器遍历就可以用迭代器for (auto e : It){cout << e << " ";}cout << endl;//sort(It.begin(), It.end());//不支持,要求随机迭代器string s("dadadaddwdxsxswx");cout << s << endl;sort(s.begin(), s.end());//算法进行排序cout << s << endl;//按照ASCII码表进行排序}
  1. 插入(emplace_back和push_back的区别)
struct A
{
public:
//普通构造函数:带默认参数,初始化成员_a1,_a2
A(int a1 = 1, int a2 = 1)//如果不传递参数则默认使用a1=1,a2=1
:_a1(a1)
, _a2(a2)
{
cout << "A(int a1=1,int a2=1)" << endl;
}
//拷贝构造函数:用已有的对象aa初始化新对象
A(const A& aa)
:_a1(aa._a1)
,_a2(aa._a2)
{
cout << "A(const A& aa)" << endl;
}
int _a1;
int _a2;
};
void test_list2()
{
list<A>It;//定义一个存储A类型对象的list容器A aa1(1, 1);//调用普通构造函数,创建aa1对象,打印A(int a1=1,int a2=1)It.push_back(aa1);//push_back接收对象,会先拷贝aa1生成临时对象,再将临时对象插入容器//调用拷贝构造函数打印出A(const A& aa)It.push_back(A(2, 2));//调用普通构造函数创建临时对象A(2,2)  打印出A(int a1-1,int a2=1)//再调用拷贝构造函数,将临时对象拷贝到容器,打印:A(const A& aa)//It.push_back(3, 3);//这样写会报错It.emplace_back(aa1);//直接在容器内部构造对象,通过拷贝构造初始化打印A(const A& aa)It.emplace_back(A(2, 2));//调用普通构造函数创建A(2,2),打印A(int a1=1,int a2=1)//再调用拷贝构造函数,在容器内部构造,打印A(const A& aa)It.emplace_back(3, 3);//直接传构造A对象的参数emplace_back//直接在容器内部调用普通构造函数参数(3,3)打印出A(int a1=1,int a2=1)cout << endl;/*list<int>It;It.push_back(1);It.emplace_back(1);It.emplace_back(2);It.emplace_back(3);It.emplace_back(4);It.emplace_back(5);*///for (auto e : It)//{//	cout << e << " ";//}//cout << endl;}

看注释了解~

  1. 插入,删除和查找
void test_list3()
{
list<int>It;It.push_back(1);It.push_back(2);It.push_back(3);It.push_back(4);It.push_back(5);auto it = It.begin();int k = 3;while (k--){++it;}It.insert(it, 30);//在第三个位置插入30for (auto e : It){cout << e << " ";}cout << endl;int x = 0;cin >> x;//find是用算法实现的it = find(It.begin(), It.end(), x);//在迭代器区间内部找到输入的x值并且删除if (it != It.end()){It.erase(it);}for (auto e : It){cout << e << " ";}cout << endl;}
  1. 排序和逆置
void test_list4()
{
list<int>It;It.push_back(1);It.push_back(2);It.push_back(3);It.push_back(4);It.push_back(5);for (auto e : It){cout << e << " ";}cout << endl;//It.reverse();//逆置方式1//reverse(It.begin(), It.end());//逆置方式2It.sort();//排序,升序//降序 仿函数//less<int>ls;//升序//greater<int>gt;//降序It.sort(greater<int>());for (auto e : It){cout << e << " ";}cout << endl;}
  1. 链表接连
void test_list5()
{
//一个链表节点转移给另一个链表
std::list<int>mylist1, mylist2;std::list<int>::iterator it;for (int i = 1; i <= 4; i++)mylist1.push_back(i);//mylist1:1 2 3 4 for (int i = 1; i <= 3; ++i)mylist2.push_back(i * 10);//mylst2:10 20 30it = mylist1.begin();++it;mylist1.splice(it, mylist2);//mylist1:1 10 20 30 2 3 4 //mylist2:empty//it 指向2的位置//用来调整当前节点的数据顺序list<int>It;It.push_back(1);It.push_back(2);It.push_back(3);It.push_back(4);It.push_back(5);for (auto e : It){cout << e << " ";}cout << endl;int x = 0;cin >> x;it = find(It.begin(), It.end(), x);//找到x的数据if (it != It.end()){//It.splice(It.begin(),It,it);//从It上截取it指向的位置的数据插入到It.begin()的位置It.splice(It.begin(), It, it, It.end());//在It上截取it指向位置的数据一直截取到整段数据结束,插入到It.begin的位置}for (auto e : It){cout << e << " ";}cout << endl;}

底层实现

刚刚我们已经认识到了list的常见的几个接口接着来实现list的模拟实现
这里我们先来打一个结构:
实现链表需要一个哨兵位,哨兵位指向一个一个的单独节点,这里的单独节点我们将用一个类模板定义一个结构来搭建。这里我们定义一个名为list_node 的类,用于表示链表的节点,每一个节点都有自己存储的数据T_data,类型由模板参数T决定。前驱节点的指针list_node<T>* _prev,后继节点的指针list_node<T>* _next,实现双向链表的结构。
在命名空间Keda下,定义一个双向链表的节点类list_node和list的框架,利用模板实现泛型,可以存储任意类型的数据。
然后实现一个无参数的初始化,也就是让_head的前驱指针和后继指针都指向_head;

#pragma once
namespace Keda
{
template<class T>struct list_node{T_data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& data=T())//也可以考虑提供默认构造,缺省参数要给T的匿名对象:_data(data),_next(nullptr),_prev(nullptr){}};template<class T>class list{typedef list_node<T>Node;//用来简化,将类名缩短,用于后续编码public:typedef list_iterator<T>iterator;iterator begin(){return _head->_next;}iterator end(){return _head;}list({_head=new node{T());//需要实现合适的默认构造,传参要传匿名对象,适用于多种类型_head->next=_head;_head->prev=_head;}private:Node* _head;//哨兵位的头节点};}

迭代器设计遍历链表:

实现逻辑:逐步解析

  1. 为什么用原生指针访问下一个节点在list中做不到?

下标加上[]不是所有容器都可以支持其遍历,迭代器可以完美的兼容所有容器,这里是链表构造,++下一个节点遍历不到,因为节点和节点之间的地址是不连续的,所以考虑用运算符重载进行封装。
用原生的指针解引用就是当前节点,++之后是下一个位置,比如说vector和string,但是链表做不到,所以链表要遍历拿到下一个节点位置,就需要用一个类去封装,然后operator* 操作挖掘到下一个节点的位置。

  1. 这里为什么用struct封装类,用class可以吗?

struct纯公有,就可以不断去访问_next,_prev等,如果用class ,属于私有,还要用友元函数,无形增加了很多不必要的麻烦。

  1. 实现逻辑逐步解析

声明一个结构体list_iterator用于实现链表的迭代器,再声明一个指针_node,指向list_node<T>类型的节点(即迭代器当前指向的链表节点)
重载解引用运算符* ,这个运算符的作用是当对迭代器执行*操作时,返回当前节点中存储的数据的引用(T&可以直接修改容器中的元素,避免拷贝开销)
Self& operator++重载前置递增运算符++,返回自身引用以支持连续递增操作

实现一个list_operator(Node* node)构造函数声明,参数Node* node是一个指针,指向list_node<T>类型的节点,当创建一个list_iterator迭代器对象时,必须给他指定一个链表节点,构造函数会让这个迭代器指向指定的节点,这样迭代器后续就会基于这个节点去遍历,访问元素了。

template<class T>struct list_iterator{typedef list_node<T> Node;//定义一个别名便于后续代码简写typedef list_iterator<T> Self;//方便在结构体内部引用自身类型Node* _node;//声明一个指针_node,指向list_node<T>类型的节点(迭代器当前指向的链表节点)list_iterator(Node*node):_node(node){}T& operator*()//重载解引用运算符* {return _node->_data;}Self& operator++()const{_node=_node->_next;return *this;}Self& operator--()const{_node=_node->prev;return *this;}Self& operator--(int){Self tmp(*this);_node=_node->_prev;return tmp;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}}
list_node(const T& data=T())//也可以考虑提供默认构造,缺省参数要给T的匿名对象这样设置的逻辑是什么?

1.对于内置类型(int,double等):T()就是0,0.0,或者nullptr
2.如果是自定义类型就要调用默认构造,string,vector等

重载迭代器比较等于与否

bool operator!=(const Self& other)
{
return _node!=other._node;
}
bool operator==(const Self& other)
{
return _node==other._node;
}

迭代器还要模拟实现operator->,因为它模拟的是指针

T* operator->()
{
return &_node->_data;
}
struct AA
{
int _a1 = 1;
int _a2 = 1;
};
void test_list1()
{
list<int>It;It.push_back(1);It.push_back(2);It.push_back(3);It.push_back(4);It.push_back(5);list<int>::iterator it = It.begin();while (it != It.end()){cout << *it << " ";++it;}cout << endl;list<AA>Ita;Ita.push_back(AA());Ita.push_back(AA());Ita.push_back(AA());Ita.push_back(AA());list<AA>::iterator ita = Ita.begin();while (ita != Ita.end()){//cout << (*ita)._a1 << ": " << (*ita)._a2 << endl;cout << ita->_a1 << ": " << ita->_a2 << endl;++ita;}cout << endl;}}

为什么这么奇怪?
实际上这有两个箭头,cout<<ita.operator->()->_a1<<" :" <<ita.operator->()->_a2<<endl;ita.operator->()实现的是调用返回AA*,再去解引用返回AA中_a1和_a2的值,所以这里做了特殊处理,本来是两个,为了可读性,省略了一个。

实现const_iterator和iterator的操作

频繁定义可修改的普通迭代器iterator和不可修改的const_iterator迭代器,会让整个代码都显得冗余,所以在这里看看库里面的源代码是如何实现的?

typedef _list_iterator<T,T&, T*>//这个是针对普通迭代器来说
typedef _list_iterator<T,const T&,const T*>//const_iterator
template<class T,class Ref,class Ptr>
typedef Ptr pointer;
typedef Ref reference;
reference operator* (){return (*node).data;}
pointer operator*()const {return &(operator*());}

假设数据类型是T,同一个类模板,增加两个模板参数来实现,这样就不用写两个类了。

链表尾插

void push_back(const <T>& x){Node* newnode=new node(x);Node* tail=_head->_prev;tail->_next=newnode;newnode->_prev=tail;_head->_prev=newnode;newnode->_next=_head;++_size;}void insert(iterator pos,const <T>& x){Node* cur=pos._node;Node* prev=cur->_prev;Node* newnode=new node(x);//prev newnode curcur->_prev=newnode;newnode->_next=cur;newnode->_prev=prev;prev->_next=newnode;++_size;}

实现头插尾插可以直接复用insert

void push_front(const<T>& x){	insert(begin(),x);}void push_back(const <T>& x) {	insert(end(),x);}

删除节点的指针

void erase(iterator pos)
{
assert(pos!=end());//删除不可以删除掉哨兵位的头节点
Node* prev=pos._node->prev;
Node* next=pos._node->next;
prev->_next=next;
next->_prev=prev;
delete pos._node;
--_size;
}

尾删头删

void pop_back()
{
erase(--end());//end()是链表的最后一个节点,前置--删除掉
}
void pop_front()
{
erase(begin());
}

总结:本节最重要的就是迭代器对节点的指针进行封装实现,需要认真了解。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/940386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025气泡膜机优质厂家推荐:瑞康机械,高效生产与定制服务兼备!

2025气泡膜机优质厂家推荐:瑞康机械,高效生产与定制服务兼备!随着电子商务和物流行业的迅猛发展,包装材料的需求量也在不断增加。气泡膜作为一种重要的缓冲包装材料,其生产设备——气泡膜机/气泡膜制袋机/高速气泡…

音视频编解码全流程之用Extractor后Decodec - 实践

音视频编解码全流程之用Extractor后Decodec - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

P8817 [CSP-S 2022] 假期计划 解题笔记

给一个不用dp的做法 solution 考虑朴素做法。 预处理出 \(f(x)\),表示距离 \(x\) 不超过 \(k\) 的点。 枚举每个景点 \(a\), \(b\), \(c\), \(d\),通过预处理出的 \(f(x)\) 计算是否合法,更新答案。 这样时间复杂度…

2025年塑料托盘厂家推荐排行榜,网格川字/九脚/田字/双面塑料托盘,平板/吹塑/注塑/焊接/印刷/组装款/高矮脚/反川字/立体库托盘公司精选!

2025年塑料托盘厂家推荐排行榜:网格川字/九脚/田字/双面塑料托盘,平板/吹塑/注塑/焊接/印刷/组装款/高矮脚/反川字/立体库托盘公司精选!随着物流和仓储行业的快速发展,塑料托盘作为重要的物流工具,其需求量逐年增…

20243866牛蕴韬类和对象作业

https://files.cnblogs.com/files/blogs/847621/20243866牛蕴韬类和对象作业.zip?t=1760859411&download=true

【动手学深度学习PyTorch】softmax回归 - 实践

【动手学深度学习PyTorch】softmax回归 - 实践2025-10-19 15:38 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: …

简单学习Typora

Markdown学习 标题: 二级标题 三级标题 字体 字体左右两边加两个星号xx就直接变为粗体 字体左右两边加一个星号xx就直接变为斜体 字体左右两边加三个星号xx就直接又斜体又加粗 hello 引用符号为引用分割线三根线或者是…

Gamma 函数

闲话(中文) 河溢危,禾已萎,鹤依偎。禾异味,鹤已畏,合一,谓何?"异味?"何矣,味何?以萎。何异胃颌已危,何医为?河易为河医。为何?医喂荷以维何一胃。何已维。"颌医未。"何矣,胃颌易维…

物理感知 RTL 合成

1、PAS:缩短设计闭环的先锋技术 物理感知合成(PAS)将物理设计信息(如布局、连线、拥塞、功耗)提前纳入 RTL 合成阶段,使合成结果与后端布局更一致,从而减少反复迭代,提升设计效率与 PPA(性能-功耗-面积)表现…

在线p图(PhotoShop网页版)加滤镜,3步搞定唯美照片

在当今生活中,分享精美照片已成为我们日常的一部分。无论是诱人的美食、精致的自拍,还是旅途中的风景,一张风格独特、色彩动人的照片总能迅速赢得朋友们的点赞。其实,想要修图加滤镜,不必再安装笨重的软件——只需…

24_envoy_配置静态资源路由

Envoy配置静态资源路由完全指南 总起:Envoy静态资源路由的重要性与挑战 在现代Web应用架构中,静态资源(如HTML、CSS、JavaScript、图片等)的高效分发是提升用户体验的关键因素。Envoy作为云原生时代的高性能代理,…

2025年冷却塔厂家推荐排行榜,闭式/方形/工业/全钢/凉水/圆形/玻璃钢/防腐冷却塔公司推荐!

2025年冷却塔厂家推荐排行榜,闭式/方形/工业/全钢/凉水/圆形/玻璃钢/防腐冷却塔公司推荐!随着工业和建筑行业的快速发展,冷却塔作为关键的热交换设备,在各个领域中发挥着重要作用。为了帮助筛选冷却塔、闭式冷却塔…

AT_toyota2023spring_final_g Git Gud

AT_toyota2023spring_final_g Git Gud (tsinsen Di6ns) 图论、树上问题、贪心、Ad-hoc定义:一个点度数 \(deg_u\) 为被合并次数;一个点集度数 \(deg_S\) 为点集内点与点集外点连边数(一条边即一次合并) 结论1:合并…

实用指南:85-dify案例分享-不用等 OpenAI 邀请,Dify+Sora2工作流实测:写实动漫视频随手做,插件+教程全送

实用指南:85-dify案例分享-不用等 OpenAI 邀请,Dify+Sora2工作流实测:写实动漫视频随手做,插件+教程全送pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displa…

uml九大图 - 作业----

uml九大图统一建模语言(UML)九大图详解 统一建模语言(UML)是一种标准化的建模语言,广泛应用于软件工程领域,用于对软件密集型系统进行可视化、详述、构造和文档化。它如同建筑师的蓝图,为软件开发团队提供了一套…

GapBuffer高效标记管理算法

目录引言GapBuffer 基本思想基本操作基于下标映射的标记记录法下标映射搜索维护对比总结 引言 最近笔者正在优化 Android 开源代码编辑器项目 TextWarrior 的一些算法,包括时间、空间两方面。TextWarroir 的文本编辑器…

2025年变位机厂家推荐排行榜,焊接变位机,双轴变位机,高精度智能变位机公司推荐!

2025年变位机厂家推荐排行榜,焊接变位机,双轴变位机,高精度智能变位机公司推荐!随着工业自动化和智能制造的快速发展,变位机、焊接变位机和双轴变位机在制造业中的应用越来越广泛。这些设备不仅能够提高生产效率,…

stable-virtio

https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/

2025年中医师承与确有专长培训机构推荐榜单:权威认证,传承经典,专业师资助力中医梦想!

2025年中医师承与确有专长培训机构推荐榜单:权威认证,传承经典,专业师资助力中医梦想!随着中医药事业的蓬勃发展,越来越多的人开始关注并投身于中医的学习和实践。中医师承与确有专长培训作为培养中医人才的重要途…

从数学概念到图像识别,再到 CNN 的联系

在矩阵论和信号处理中,奇异值分解(Singular Value Decomposition, SVD) 是一个极其重要的工具。它不仅是一个数学分解公式,更是连接数据压缩、特征提取和深度学习优化的桥梁 。 矩阵与奇异值的定义对任意矩阵 \(A …