【C++_list】理解链表!实现链表!成为链表!!

news2024/11/18 15:24:04

List

  • 1. list的介绍及使用
  • 2. list的模拟
    • 1)大致了解List框架
    • 2)模拟实现List操作
    • 3)关于const迭代器的问题(重点)
    • 4)关于链表拷贝的问题

1. list的介绍及使用

  • 下面会给出list的文档介绍官网,也是本博客的参考网址~
    list的文档介绍
  • 难点一:list的迭代器与我们往常的迭代器不同
    链表是双向迭代器,只支持++ – 不支持 ±
    欸?双向迭代器和随机访问迭代器是什么? -> 一张图帮你搞定!
    在这里插入图片描述
    在这里插入图片描述
  • 难点二:List不支持sort
//记住sort的头文件是这个 --> #include<algorithm>
	list<int> lt1 = { 1,2,3,4,5,6 }; //定义
	sort(lt1.begin(), lt1.end());  //这样写是错误的

🔺在 list 里面,我们是不能使用 sort 进行排序的,这里运用到难点一的知识点:因为sort使用的是随机访问迭代器
在这里插入图片描述
其实当我们点开 list 文档发现,他已经帮我们写好了
在这里插入图片描述
所以直接调用即可❤
(注意,我们现在讲的是list的运用,如何实现在后面很快就会说明)

	list<int> lt1 = { 1,2,3,4,5,6 }; //定义
	lt1.sort();

在这里插入图片描述
这里有一个需要注意的是,sort默认的算法是升序,如果想要降序的话,我们需要引入一个算法 greater<int>
🔺降序实现方法如下:

	list<int> lt1 = { 1,2,3,4,5,6 }; //定义
	lt1.sort(greater<int>()); //只需要在这里加上就可以了
  • 随机调一个数放到首位
    在这里插入图片描述
  1. 找出要放在首位的数
    记住:在 List 里面是没有find函数
auto it = find(mylist1.begin(), mylist1.end(), 3)
//这里的 3 是我们要找出的数
  1. 将找出的数粘贴到首位
    这里引出一个 splice(可以将链表的一个元素插入到其他地方)

🔺代码实现一下:

std::list<int> mylist1;
for(int i = i; i <= 4; i++)
	mylist1.push_back(i);
auto it = find(mylist1.begin(), mylist.end(), 3);
//下面便是splice的使用
mylist1.splice(mylist1.begin(), mylist1, it);

在这里 splice 并没有删除链表元素然后在插入,类似于剪切功能
请添加图片描述

欧克欧克,其实list还是比较简单的,那么接下来我们来实现一下,来加深影响吧!

请添加图片描述


2. list的模拟

前提是,我们实现的是List的双向带头循环

1)大致了解List框架

实现List的前提是,我们需要弄懂List的内部构造
请添加图片描述
🔺代码模板可以写成这样

template<class T>
struct ListNode
{};

template<class T>
class list
{};
  • 节点包括:指向前一个节点;指向后一个节点;该节点的值
template<class T>
struct ListNode
{
	ListNode<T>* _next;
	ListNode<T>* _prev;

	T _data;
};

注意:为什么是用struct而不是class呢?
因为我们在使用LIst的时候会经常访问Node节点,而class默认是私有,所以在访问的时候会报错。(总而言之就是公有私有的问题)

  • 链表里面包含一个头指针(头插尾插操作忽略)
template<class T> 
class list
{
	//将ListNode定义为Node为方便后续代码理解
	typedef ListNode<T> Node;
private:
	Node* _head;
};

2)模拟实现List操作

具体代码实现方位在如下图位置在这里插入图片描述

要实现的函数:
1. 构造函数–初始化
2. 析构函数–销毁
3. push_back–尾插
4.insrt & erase – 插入和删除
5. iterator–迭代器

  • 构造函数
list()
{
	_head = new Node;
	_head->_next = _head; 
	_head->_prve = _head; 
}
  • 析构函数
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

void clear()
{
	auto it = begin();
	while (it != end())
	{
		erase(it);
	}
}
  • push_back
void push_back(const T& x)
{
	Node* newnode = new Node(x); 
	Node* tail = _head->_prev;

	tail->_next = newnode; 
	newnode->_prev = tail;
	newnode->_next = _head;
	_head->_prev = newnode;
}

在这里插入图片描述

请添加图片描述
注意:即使是在空链表只有头节点的情况下也同样适用
在这里插入图片描述

  • 问题一:目前我们已经初步的实现了 List 但是我们在调用的时候会出现报错
  • 在这里插入图片描述
  1. 加入ListNode 的默认构造
ListNode(const T& data = T())
	:_next(nullptr)
	,_prev(nullptr)
	,_data(data)
{}
  1. 加入匿名对象,是之初始化后不是空
list()
{
	_head = new Node(T());
	_head->_next = _head; 
	_head->_prev = _head; 
}
  • insert & erase
    1. insert:在pos节点前插入
      在这里插入图片描述
      2.erase:在pos位置删除

代码实现如下

iterator insert(iterator pos, const T& x)
{ 
	Node* cur = pos._node;
	Node* newnode = new Node(x);
	Node* prev = cur->_prev;
	//  prev    newnode    cur
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;

	//返回新插入的迭代器  
	return iterator(newnode);
}

iterator erase(iterator pos)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->next;

	prev->_next = next;
	next->_prev = prev;

	delete cur;

	//有迭代器失效
	//erase后,pos失效,pos指向的节点被释放
	return iterator(next)
}
  • iterator 重点🔺
    在List里面每个节点在物理结构上面都不是连续的,所以单凭迭代器++是无法完成遍历的,所以在这里我们需要单独构造

在这里插入图片描述
我们需要一个类来定义迭代器

template<class T>
class ListIterator
{};
  • 我们需要搞清楚的是begin和end
    在这里插入图片描述
  • 这一part比较简单,所以直接上代码即可
/*
当给定的iterator不能满足链表的时候
我们需要自己创建一个新的迭代器  
*/
template<class T>
class ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T> self;

	Node* _node;
public:
	//构造函数,给定随机值
	ListIterator(Node* node)
		:_node(node)
	{}

	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	//前置,返回++之前的值
	self& operator++(int)
	{
		self tmp(*this);
		//Self tmp = *this;
		_node = _node->_next;
		return tmp;
	}

	self& operator--(int)
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	 
	T& operator*()
	{
		return _node->_data;
	}

	bool operator!=(const self& it)
	{
		return _node != it._node;
	}

	bool operator==(const self& it)
	{
		return _node == it._node;
	}
};


🔺其实是因为List的迭代器不能仅凭 iterator++ 来解决问题(因为地址的随机性)

//随后在class list 里面便可以定义后变成我们所需的iterator
typedef ListIterator<T> iterator;

在这里插入图片描述

  • 难点三:不需要对迭代器写析构函数
    迭代器的意义是希望我们去访问或者修改节点,而并不是去删除这个节点,换句话来说,迭代器并不是List的本体,而是一个访问作用的工具。

具象化来说,大体应该是下面这种关系~
在这里插入图片描述

  • 难点四:不需要拷贝构造(深拷贝)
    在这里插入图片描述
    一般一个类不需要显示的写析构函数,也就不需要写构造函数

3)关于const迭代器的问题(重点)

当我们创建一个const的链表的时候,发现当我们继续遍历链表的时候就会报错,对应的我们需要一个const 的迭代器~
请添加图片描述

在这里插入图片描述

🔺问题二;那么我们怎样才能构造一个const的迭代器呢?

  1. 误区一:迭代器不能普通迭代器前面加 const 修饰
    const iterator const
    在这里插入图片描述
    我们希望const的是迭代器指向的内容不能更改(*iterator),而不是迭代器本身(iterator)
    在这里插入图片描述
  • 🔺其实解决办法也很简答,我们只要控制返回的类型是 T* ,这样就能保证const 的是所指向的值
  • 那么这样我们需要多创建一个类
typedef ListIterator<T> iterator;
typedef ListConstIterator<T> const_iterator;

在这里插入图片描述
在这里插入图片描述
他们的区别就只有一点:
在这里插入图片描述
其他的代码部分都是一模一样的

既然这样的话,我们可以有第二种方式,这里就放一张图让大家理解一下:

在这里插入图片描述
上面的代码大大减少了代码的重复率,使之一份代码可以二次使用


4)关于链表拷贝的问题

由于默认拷贝构造是浅拷贝,所以我们很容易遇到这样这个问题
在这里插入图片描述
我们创建一个 lt2 ,当 lt1 插入一个数的时候,lt2 也插入了
此时两个链表是共享的而不是互相独立的
在这里插入图片描述

深拷贝代码实现

list(const list<T>& lt)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	
	for (auto e : lt)
	{
		push_back(e);
	}
}

感谢大家的支持,这次暑假过后也有很多事情耽搁了博客
如果喜欢的话请多多支持我
请添加图片描述

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

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

相关文章

Vue常用指令及其生命周期

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 目录 1.常用指令 1.1 v-bind 1.2 v-model 注意事项 1.3 v-on 注意事项 1.4 v-if / v-else-if / v-else 1.5 v-show 1.6 v-for 无索引 有索引 生命周期 定义 流程 1.常用指令 Vue当中的指令…

【OpenCV C++20 学习笔记】基本图像容器——Mat

【OpenCV C20 学习笔记】基本图像容器——Mat 概述Mat内部结构引用计数机制颜色数据格式 显式创建Mat对象使用cv::Mat::Mat构造函数矩阵的数据项 使用数组进行初始化的构造函数cv::Mat::create函数MATLAB风格的初始化小型矩阵通过复制创建Mat对象 Mat对象的输出其他普通数据项的…

软考:软件设计师 — 5.计算机网络

五. 计算机网络 1. OSI 七层模型 层次名称主要功能主要设备及协议7应用层实现具体的应用功能 POP3、FTP、HTTP、Telent、SMTP DHCP、TFTP、SNMP、DNS 6表示层数据的格式与表达、加密、压缩5会话层建立、管理和终止会话4传输层端到端的连接TCP、UDP3网络层分组传输和路由选择 三…

Spring事件机制

文章目录 一、Spring事件二、实现Spring事件1、自定义事件2、事件监听器2.1 实现ApplicationListener接口2.2 EventListener2.3 TransactionalEventListener 3、事件发布4、异步使用 三、EventBus1、事件模式2、EventBus三要素3、同步事件3.1 定义事件类3.2 定义事件监听3.3 测…

vscode回退不显示了,不方便操作

一、后退前进按钮 顶部显示&#xff0c;方便调试 <—— ——> 文件-> 首选项 -> 设置->commandcenter->勾选 Window: Title Bar Style->custom 将native —>custom

STM32是使用的内部时钟还是外部时钟

STM32是使用的内部时钟还是外部时钟&#xff0c;经常会有人问这个问题。 1、先了解时钟树&#xff0c;见下图&#xff1a; 2、在MDK中&#xff0c;使用的是HSEPLL作为SYSCLK&#xff0c;因此需要对时钟配置寄存器&#xff08;RCC_CFGR&#xff09;进行配置&#xff0c;寄存器内…

Linux:传输层(2) -- TCP协议(2)

目录 1. 流量控制 2. 滑动窗口 3. 拥塞控制 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. TCP异常情况 1. 流量控制 接收端处理数据的速度是有限的. 如果发送端发的太快 , 导致接收端的缓冲区被打满 , 这个时候如果发送端继续发送 , 就会造成丢包, 继而引…

享元模式(结构型)

目录 一、前言 二、享元模式 三、总结 一、前言 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;用于减少大量细粒度对象的内存占用。它通过共享尽可能多的相同数据来节约内存空间。 享元模式由以下角色组成&#xff1a; Flyweight&…

【OpenCV C++20 学习笔记】扫描图片数据

扫描图片数据 应用情景图像数据扫描的难点颜色空间缩减&#xff08;color space reduction&#xff09;查询表 扫描算法计算查询表统计运算时长连续内存3种扫描方法C风格的扫描方法迭代器方法坐标方法LUT方法 算法效率对比结论 应用情景 图像数据扫描的难点 在上一篇文章《基…

项目一缓存商品

文章目录 概要整体架构流程技术细节小结 概要 因为商品是经常被浏览的,所以数据库的访问量就问大大增加,造成负载过大影响性能,所以我们需要把商品缓存到redis当中,因为redis是存在内存中的,所以效率会比MySQL的快. 整体架构流程 技术细节 我们在缓存时需要保持数据的一致性所…

AHK是让任何软件都支持 Shift + 鼠标滚轮 实现界面水平滚动

目录 基本介绍 详细特点 图解安装 下载失败&#xff1f;缓慢&#xff1f; 创建并运行脚本代码&#x1f603; 新建空 xxx.ahk文件 vscode/记事本等编辑工具打开 复制并粘贴简易脚本 运行 其他问题 问题一&#xff1a;弹出无法执行此脚本 关闭脚本 基本介绍 AutoHot…

zookeeper开启SASL权限认证

目录 一、SASL介绍 二、使用 SASL 进行身份验证 2.1 服务器到服务器的身份验证 2.2 客户端到服务器身份验证 三、验证功能 一、SASL介绍 默认情况下&#xff0c;ZooKeeper 不使用任何形式的身份验证并允许匿名连接。但是&#xff0c;它支持 Java 身份验证与授权服务(JAAS)…

用户需要什么-软件的工程可用性(第一部分)01

Larry L. Constantine 著&#xff0c;Huang Yin 译 “究竟用户的需要是什么&#xff1f;”如果 Fred 作为一个程序员而不是一个心理学家时他可能会提出这 样一个问题。用户们通常需要更多&#xff0c;而开发人员似乎看上去并不能很好的领会并更好的满足他们。对于我们而言&…

WPF使用TouchSocket实现Tcp client

文章目录 前言1、页面展示2、主页面UI代码2、TCP client的UI代码3、Tcp client后台代码实现4、UI与后台代码的关联 前言 在该篇的Demo中&#xff0c;您可以找到以下内容&#xff1a; 1、TouchSocket的使用&#xff1b; 2、CommunityToolkit.Mvvm的使用&#xff1b; 3、AvalonD…

IF=8.5 MIMIC-IV高阶玩法!中国用新指标SHR+机器学习拿一区top,思路太牛了

‍ MIMIC-IV 发文难&#xff1f;那是你还没遇到对的思路&#xff01;如今机器学习数据库挖掘的文章层出不穷&#xff0c;今天介绍的这篇文章是在MIMIC-IV数据库的基础上&#xff0c;用了一个新指标—应激性高血糖比&#xff08;SHR&#xff09;&#xff0c;结合机器学习构建预测…

【iOS】——Block循环引用

循环引用原因 如果在Block中使用附有_ _strong修饰符的对象类型自动变量&#xff0c;那么当Block从栈复制到堆时&#xff0c;该对象为Block所持有&#xff0c;这样容易引起循环引用。 HPPerson *person [[HPPerson alloc] init];person.block ^{NSLog("person.age--- …

Redis使用场景-热点数据缓存

什么是缓存&#xff1f; 为了把一些经常访问的数据放入缓存中已减少对数据库的访问&#xff0c;从而减少数据库的压力&#xff0c;提高程序的性能。【内存中存储】-效率快 缓存的原理 什么样的数据适合放入缓存中&#xff1f; 1.查询频率高且修改频率低 2.数据安全性低 哪些组件…

《python语言程序设计》第6章第7题财务应用程序:计算未来投资,编写函数计算制定年数以给定利率

记住这里增加循环应该是以年为单位。但是添加的数是月为单位 此处需留意其实点不是1&#xff0c;1代表1年&#xff0c;这里月所以其实是12&#xff0c;这里的单位是月&#xff0c;而不是年。 python for i in range(12,monthNum12,12) 如果你把12都换成1呢&#xff1f;&…

本地生活抽佣系统搭建:如何让系统具有竞争优势?

随着本地生活的潜力不断展现&#xff0c;本地生活服务商逐渐成为新兴职业中的一大热门&#xff0c;本地生活抽佣系统搭建的热度也一直保持着飙升的状态。 抖音生活发布的《2023年数据报告》显示&#xff0c;2023年&#xff0c;抖音生活服务平台总交易额增长256%&#xff0c;抖…

监测Nginx访问日志状态码,并做相应动作

文章目录 引言I 监测 Nginx 访问日志情况,并做相应动作1.1 前提准备1.2 访问日志 502 情况,重启 bttomcat9服务1.3 其他案例:访问日志 502 情况,重启 php-fpm 服务II 将Shell 脚本check499.sh包装成systemd服务2.1 创建systemd服务2.2 配置service2.3 开机启动2.4 其他常用…