STL:List从0到1

请添加图片描述

在这里插入图片描述

🎉个人名片

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————

🎉文章简介

🎉本篇文章将 介绍如何使用C++编写代码来实现一个类似于STL中的List容器 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————

一.前言

这篇文章将介绍如何使用C++编写代码来实现一个类似于STL中的List容器。 list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器。在这篇文章中,你将学习并实现List的常见功能,如添加元素、删除元素等。通过实现自己的List容器,你将更好地熟悉List的使用及相关特性,并提升对C++语言的理解和掌握。
————————————————

二.List的介绍

List文档介绍链接: link

1. list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代;
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素;
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效;
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好(不需要挪动数据);
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素);

三.List的结构及模拟实现

一.底层结构

List底层是一个带头双向循环链表

如图:
在这里插入图片描述
在这里插入图片描述
库里面的begin() 与end() 返回的节点位置:

begin()返回的是头节点的下一个节点;
而end()返回的是头节点;

二.List的模拟实现

重点::迭代器的实现

1. 构造函数
template<class T>
struct ListNode
{
	ListNode<T>(const T& x=T())    
		:_next(nullptr)
		,_prev(nullptr)
		,_val(x)
	{
	}
	ListNode<T>* _next;
	ListNode<T>* _prev;
	T _val;
};

库里面的List类的构造函数是另写一个函数,因为这个函数拷贝构造会使用,然后复用它,所以我们也这样实现;

template<class T>
class List
{
public:
	void empty_List()
	{
		_phead = new node;
		_phead->_next = _phead;
		_phead->_prev = _phead;
	}
	List()
	{
		empty_List();
	}
}
2. 拷贝构造函数
List(const List<T>& lt)
{
	empty_List();     //初始化
	for (const auto& e : lt)    
	{
		push_back(e);        //将lt里面的数据依次尾插
	}
}
3. 插入函数

思路:记录前一个和后一个节点,然后连接

在这里插入图片描述

iterator insert(iterator pos, const T& x)
{
	node* newnode = new node(x);    //构造一个节点
	node* next = pos._node;         
	node* prev = next->_prev;      //记录前一个
	newnode->_next = next;         
	next->_prev = newnode;         //链接
	newnode->_prev = prev;
	prev->_next = newnode;

	return pos;
}
4. 尾插函数

复用insert函数

void push_back(const T& x)
{
/*	node* newnode = new node(x);
	node* tail = _phead->_prev;
	tail->_next = newnode;
	newnode->_prev = tail;       //不复用的写法
	newnode->_next = _phead;
	_phead->_prev = newnode;*/

	insert(end(), x);
}
5. 头插函数

复用insert函数

	void push_front(const T& x)
	{
		insert(begin(),x);
	}
6. 删除函数
iterator 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;                  //释放掉该节点

	return next;                       //返回删除元素的下一个节点
}
7. 尾删函数

复用删除函数

	void pop_back()
	{
		//erase(end()._node->_prev);
		erase(--end());       //头节点的前指针指向的是最后一个节点

	}
	
8. 头删函数

复用删除函数

	void pop_front()
	{
		erase(begin());     
	}
9. 迭代器的实现

因为链表的底层物理空间不是连续的,所以不能使用原生指针类实现。因为原生指针++,可以找到下一个数据,但是链表的节点与节点之间不是连续的,指针++,不能找到下一个节点,所以我们需要操作符重载,改变 ++, != ,* 等操作符的行为;又因为节点指针是内置类型,不能进行操作符重载,所以我们只能将它进行封装,封装在一个类里面,进行重载;

template<class T>
struct __List_iterator       
{
	typedef ListNode<T> node;
	typedef __List_iterator<T> self;

	__List_iterator(node* node)       //构造函数
		:_node(node)
	{}
	self& operator++()               //运算符的重载
	{
		_node = _node->_next;       //前置++,返回++后的值
		return *this;
	}
	self& operator++(int)
	{
		self tmp(_node);         //保存++前的值
		_node = _node->_next;

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

		return tmp;
	}
	T& operator*()
	{
		return _node->_val;
	}
	bool operator!=(const self& s)
	{
		return s._node!= this->_node;
	}
	bool operator==(const self& s)
	{
		return s._node == this->_node;
	}
	node* _node;
};
10. 赋值运算符重载

传统写法:

	void clear()
	{
		iterator lt = begin();
		while (lt != end())
		{
			lt = erase(lt);
		}
	}
//lt1=lt2
List<T>& operator=(const List<T>& lt)
{
	clear();                                 //清空函数,将链表中的有效数据删除掉,保留头节点
	for (const auto& e : lt)
	{
		push_back(e);            //依次尾插
	}

	return *this;
}

现代写法:

void swap(List<T>& lt)
{
	std::swap(_phead, lt->_phead);
}
//lt1=lt2
List<T>& operator=(List<T> lt)    //lt是lt2的拷贝构造
{
	swap(lt);      //交换lt与lt1

	return *this;    //返回
}
补充知识:

typedef 放在类里面与外面的区别:
如果是放在公有里面,则类外面也可以使用,但是要指定类域;
如果是私有的话,则类外面不能使用;

三.总代码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace L
{
	template<class T>
	struct ListNode
	{
		ListNode<T>(const T& x=T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(x)
		{
		}
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _val;
	};

	template<class T>
	struct __List_iterator
	{
		typedef ListNode<T> node;
		typedef __List_iterator<T> self;

		__List_iterator(node* node)
			:_node(node)
		{}
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		self& operator++(int)
		{
			self tmp(_node);
			_node = _node->_next;

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

			return tmp;
		}
		T& operator*()
		{
			return _node->_val;
		}
		bool operator!=(const self& s)
		{
			return s._node!= this->_node;
		}
		bool operator==(const self& s)
		{
			return s._node == this->_node;
		}
		node* _node;
	};

	template<class T>
	class List
	{
	public:
		typedef ListNode<T> node;
		typedef __List_iterator<T>  iterator;

		iterator begin()
		{
			return iterator(_phead->_next);
		}
		iterator end()
		{
			return iterator(_phead);
		}

		void empty_List()
		{
			_phead = new node;
			_phead->_next = _phead;
			_phead->_prev = _phead;
		}
		List()
		{
			empty_List();
		}
		List(const List<T>& lt)
		{
			empty_List();

			for (const auto& e : lt)    //引用更好,如果T类型是自定义类型的话
			{
				push_back(e);
			}
		}
		//lt1=lt2
		List<T>& operator=(const List<T>& lt)
		{
			clear();
			for (const auto& e : lt)
			{
				push_back(e);
			}

			return *this;
		}
		void swap(List<T>& lt)
		{
			std::swap(_phead, lt->_phead);
		}
		//lt1=lt2
		List<T>& operator=(List<T> lt)
		{
			swap(lt);

			return *this;
		}

		void clear()
		{
			iterator lt = begin();
			while (lt != end())
			{
				lt = erase(lt);
			}
		}
		~List()
		{
			clear();
			delete _phead;
			_phead = nullptr;
		}


		void push_back(const T& x)
		{
		/*	node* newnode = new node(x);
			node* tail = _phead->_prev;
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _phead;
			_phead->_prev = newnode;*/

			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(),x);
		}
		iterator insert(iterator pos, const T& x)
		{
			node* newnode = new node(x);
			node* next = pos._node;
			node* prev = next->_prev;
			newnode->_next = next;
			next->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;

			return pos;
		}
		iterator 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;

			return next;
		}
		void pop_back()
		{
			//erase(end()._node->_prev);
			erase(--end());

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

	private:
		node* _phead;
	};

请添加图片描述

0

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp开发微信小程序调用打电话

在使用uniapp开发微信小程序的时候&#xff0c;经常需要调用打电话功能。 下面我们来讲解一下如何实现该功能&#xff0c;效果图请看下图&#xff1a; 代码部分&#xff1a; <!-- h5部分 --><button click"playphone()"></button><!-- JS部分 …

代码随想录day21(1)二叉树:平衡二叉树(leetcode110)

题目要求&#xff1a;判断一棵树是否为平衡二叉树 思路&#xff1a;递归地比较左右子树&#xff0c;只要有一棵子树不满足条件就说明这棵树不是平衡二叉树。本题采用迭代法较为复杂。 leetcode实战&#xff1a; 代码实现&#xff1a; 递归&#xff1a; 迭代&#xff1a;

【WebAssembly】WebAssembly概念介绍和在js中使用

简言 记录下WebAssembly的概念和在JavaScript中的使用方法。 WebAssembly官网 WebAssembly WebAssembly &#xff08;缩写为 Wasm&#xff09;是一种二进制指令格式&#xff0c;用于基于堆栈的虚拟机。Wasm 被设计为编程语言的可移植编译目标&#xff0c;可在网络上部署客户…

爬虫逆向sm3和sm4 加密 案例

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; 案例--aHR0cDovLzExMS41Ni4xNDIuMTM6MTgwODgvc3Vic2lkeU9wZW4 第一步&#xff1a;分析页面和请求方式 …

代码随想录算法训练营第39天 | 62.不同路径 , 63. 不同路径 II

动态规划章节理论基础&#xff1a; https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 62.不同路径 题目链接&#xff1a;https://leetcode.cn/problems/unique-paths/ 思路&#xff1a; 动规五部曲&#xff1a…

实战Python Socket编程:开发多用户聊天应用

实战Python Socket编程&#xff1a;开发多用户聊天应用 Python Socket 编程概述什么是Socket编程&#xff1f;Socket编程的应用场景Socket编程的重要性基本概念 环境准备Python版本必要的库开发环境配置调试工具 基本Socket编程创建Socket绑定Socket到端口监听连接接受连接发送…

基于C++的反射功能

需求&#xff1a; 利用C的发射机制&#xff0c;实现根据字符串创建实例对象。 代码&#xff1a; #ifndef OBJECT_H #define OBJECT_H#include <string> #include <map>typedef void* (*Constructor)();class CObjectFactory { public:static void registerClass…

Spring Boot轻松整合Minio实现文件上传下载功能【建议收藏】

一、Linux 安装Minio 安装 在/root/xxkfz/soft目录下面创建文件minio文件夹&#xff0c;进入minio文件夹&#xff0c;并创建data目录&#xff1b; [rootxxkfz soft]# mkdir minio [rootxxkfz soft]# cd minio [rootxxkfz minio]# mkdir data执行如下命令进行下载 [rootxxkf…

python基础——字符串的常见操作方法【下标索引,index,count,len,replace,split,strip】

&#x1f4dd;前言&#xff1a; 字符串是一种有序的&#xff0c;允许重复字符串存在的&#xff0c;不可修改的序列 这篇文章主要总结一下python中有关字符串的部分相关知识&#xff0c;以及字符串的常见操作方法&#xff1a; 1&#xff0c;和其他序列极其类似的操作方法 2&…

Three 材质纹理 (总结三)

THREE.MeshLambertMaterial&#xff08;网格 Lambert 材质&#xff09; 该材质使用基于非物理的Lambertian模型来计算反射率。可以用来创建暗淡的并不光亮的表面&#xff0c;该材质非常易用&#xff0c;而且会与场景中的光源产生反应。 MeshLambertMaterial属性 # .color : …

mysql中用逗号隔开的某字段,如何判断其他表的字段值是否在这个字段中

因为要增加需求&#xff0c;需要将线上表中老数据&#xff0c;修改为新数据的规则。 线上两张表&#xff0c;sequence_number中is_use有3作废、2到期状态&#xff0c;需要根据这个状态和school_ai_authorization中的is_deleted修改新增的state字段。 sequence_number表结构&…

数据分析实战-Python实现博客评论数据的情感分析

数据分析实战-Python实现博客评论数据的情感分析 学习建议SnowNLP基础什么是SnowNLP&#xff1f;SnowNLP情感分析 SnowNLP使用SnowNLP安装情感分析中文分词关键词提取拼音、词性标准 SnowNLP实战-博客评论数据的情感分析数据准备数据获取数据分析 总结 学习建议 现在很多网站、…

关于振弦采集仪的应用编写

instruction&#xff1a; 1、本应用基于深圳市安传物联科技有限公司所生产的八通道振弦变送器产品。该产品为MAX485 信号的变送设备&#xff0c; 并以Modbus协议输出。 2、本应用采用python语言编写。 功能实现&#xff1a; 1、发送&#xff1a; 01 03 10 00 00 02 C0 CB并…

JVM之调优(一)

背景&#xff1a;生产环境由于堆内存较大&#xff0c;fullgc 垃圾回收导致程序卡顿问题&#xff08;假死&#xff09; 目录 一、程序卡顿导致的影响 前端页面空白后端数据重复 二、解决方法 降低堆内存大小使用合适的垃圾回收器&#xff08;可以尝试&#xff0c;还未进行测试…

【python】爬取杭州市二手房销售数据做数据分析【附源码】

一、背景 在数据分析和市场调研中&#xff0c;获取房地产数据是至关重要的一环。本文介绍了如何利用 Python 中的 requests、lxml 库以及 pandas 库&#xff0c;结合 XPath 解析网页信息&#xff0c;实现对链家网二手房销售数据的爬取&#xff0c;并将数据导出为 Excel 文件的过…

捋顺【反函数求导】

设 d y d x f ( x ) 则 d x d y 1 f ( x ) 以 y t a n x 为 例 &#xff0c; d y / d x s e c 2 x , d x / d y 1 s e c 2 x c o s 2 x 到 此 为 止 &#xff0c; 似 乎 难 以 推 导 &#xff0c; 但 是 假 如 用 t a n x ( 也 就 是 y ) 将 c o s 2 x 表 示 出 来 &…

jenkins容器中安装python遇到问题

在Jenkins容器中安装配置Python时遇到问题 执行./configure --prefix/opt/python3/时遇到configure: error: no acceptable C compiler found in $PATH 这个问题就是缺少gcc编译环境。将gcc安装上即可&#xff1a; yum install -y gcc##前提是容器里的系统是cenos才可以&#…

实在智能Agent——RPA终极进化方向

RPA技术备受瞩目&#xff0c;它通过“机器人”自动化了人力执行的重复性、低复杂度任务&#xff0c;解放了员工并降低了企业成本。RPA机器人全天候运行&#xff0c;避免人为错误&#xff0c;高效处理任务&#xff0c;成为处理事务、操作数据、回应查询的理想选择。在管理后台&a…

易方达产品亏损仍存,“老鼠仓”阴影犹在,如何突出重围?

近日&#xff0c;易方达基金宣布易方达沪深300 ETF跻身“千亿规模ETF”行列&#xff0c;成为国内“ETF千亿俱乐部”的第三位成员。截至3月8日&#xff0c;该基金的规模增长112.21亿元&#xff0c;涨幅9.45%&#xff0c;规模增量在10亿以上的股票型ETF产品中排名第一。 回望202…

(网络安全)一款强大的逆向分析工具,开源!

工具介绍 Ghidra 是由美国国家安全局&#xff08;NSA&#xff09;研究部门开发的软件逆向工程&#xff08;SRE&#xff09;套件&#xff0c;用于支持网络安全任务。包括一套功能齐全的高端软件分析工具&#xff0c;使用户能够在各种平台(Windows、Mac OS和Linux)分析编译后的代…