list~模拟实现

news2024/11/15 15:46:09

目录

list的介绍及使用

list的底层结构

节点类的实现

list的实现

构造函数

拷贝构造

方法一:方法二:

 析构函数

赋值重载

insert  /  erase

push_/pop_(尾插/尾删/头插/头删)

begin和end(在已建立迭代器的基础上)

迭代器实现

迭代器类模板参数说明

list包含在头文件

 部分摘自— list模拟实现


list的介绍及使用

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

list的底层结构

ps—>2

list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素


节点类的实现

但我们要去实现list,就首先需要定义实现好一个结点类。

而一个结点需要存储:

需要存储的数据、前驱节点、后继节点的地址。

这里用struct定义这个节点类不用class,因为很多地方都要用到和访问这个节点类,成员变量就都共有。

这里也不需要专门单独写一个析构函数,因为这个节点类并没有对应的资源需要清理;

template <class T>
struct ListNode
{
	ListNode<T>* _prev;
	ListNode<T>* _next;
	T _data;

	ListNode(const T& data = T())
		:_prev(nullptr)
		, _next(nullptr)
		, _data(data)
	{}
};

list的实现

链表其实就是用一个指向头节点的指针管理起来的,所以,我们可以定义一个list类,它的成员变量是一个指向头节点的指针,因为,list的底层是一个带头双向循环链表,所以这里的指针,应该指向,带有哨兵位的头节点。

template<class T>
class list
{
public:
	/*typedef ListNode<T> Node;
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;
	list()
	{
		_head = new Node();
		_head->_prev = _head;
		_head->_next = _head;
	}

	iterator begin()
	{
		return iterator(_head->_next);
	}
	iterator end()
	{
		return iterator(_head);
	}*/

private:

	Node* _head;
};

构造函数

ist是一个带头双向循环链表,在构造一个list对象时,首先要申请一个头结点,并让其前驱指针和后继指针都指向自己。

list()
{
	_head = new node; //申请头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
}


拷贝构造

拷贝构造有两种方式:        ①用一个list构造方法二        ②迭代器区间

为了方便起见,我们可以先封装手撕一个empty()函数,去申请头节点,生成一个空链表,在构造和拷贝构造的时候,可以更加方便地调用。

void empty()
{
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;
}

方法一:

已经申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。

list(const list<T>& lt)
{
	empty();

	for (const auto& e : lt)
	{
		push_back(e);
	}
}

方法二:

我们先实现一个使用迭代器区间构造的函数。

template <class Iterator>
list(Iterator first, Iterator last)
{
	empty;//不加会出问题

	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

然后,我们先创建一个临时对象让他利用被拷贝对象的迭代器构造出来,然后再交换,被利用完后的临时对象会在栈帧结束后被清除。

我们先实现swap函数,很简单,交换头节点的指针就可以。

void swap(list<T>& tmp)
{
	std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
	empty();

	list<T> tmp(lt.begin(), lt.end());

	swap(tmp);
}


 析构函数

我们可以调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可

void clear()//将头结点释放
{
	iterator it = begin();

	while (it != end())
	{
		//it = erase(it);
		erase(it++);
	}
}
~list()
{
	clear();//将头结点释放

	delete _head;//最后将头指针置空即可
	_head = nullptr;
}


赋值重载

通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。

list<T>& operator=(list<T> tmp)
{
	swap(tmp);

	return *this;
}

insert  /  erase

insert函数的作用时在指定迭代器的位置之前插入一个数据。

iterator insert(iterator pos, const T& x)
{
	Node* node = pos._node;//记录当前节点
	Node* prev = node->_prev;//记录前驱节点

	Node* newnode = new Node(x);//记录要插入节点

	newnode->_prev = prev;
	newnode->_next = node;
	
	prev->_next = newnode;
	node->_prev = newnode;

	return iterator(newnode);
}

erase函数用来删除指定迭代器位置的数据。

iterator erase(iterator pos)
{
	assert(pos != end());//防止删除头节点
	
	Node* del = pos._node;//记录要删除节点
	Node* prev = del->_prev;//记录前驱节点
	Node* next = del->_next;//记录后续节点

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

	return iterator(next);//返回所给迭代器pos的下一个迭代器,防止迭代器失效
}

push_/pop_(尾插/尾删/头插/头删)


void push_back(const T& val) 
{ 
	insert(end(), val); 
}

void pop_back() 
{ 
	erase(--end()); 
}

void push_front(const T& val) 
{ 
	insert(begin(), val); 
}

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


begin和end(在已建立迭代器的基础上)

begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。

iterator begin()
{
	return iterator(_head->_next);
}

const_iterator begin() const
{
	return const_iterator(_head->_next);
}

iterator end()
{
	return iterator(_head);
}

const_iterator end() const
{
	return const_iterator(_head);
}


迭代器实现

对于list来说,它的数据不是连续存储的而是通过一个一个节点通过指针连接到一起的,所以,_head++并不能到下一个数据的位置,但是可以通过_head = _head->_next实现,所以这里我们可以使用节点的指针单独封装一个类,通过运算符重载模拟指针的行为。

(vector迭代器可以使用原生指针来实现,因为vector的空间时连续的,可以直接支持运算符++的重载,_start时指向第一个数据的指针,_start++就指向下一个位置了。

迭代器类模板参数说明

template<class T, class Ref, class Ptr>

 在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

所以,迭代器类的模板参数列表当中的RefPtr分别代表的是解引用类型指针类型

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	Node* _node;

	typedef ListIterator<T,Ref,Ptr> self;

	ListIterator(Node* node)
		:_node(node)
	{}

	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;
	}

	Ref operator*()
	{
		return _node->_data;
	}

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

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

	Ptr operator->()
	{
		return &_node->_data;
	}
};


struct Pos
	{
		int _row;
		int _col;

		Pos(int row = 0, int col = 0)
			:_row(row)
			,_col(col)
		{}
	};

	

	void test_list2()
	{
		list<Pos> lt1;
		lt1.push_back(Pos(100, 100));
		lt1.push_back(Pos(200, 200));
		lt1.push_back(Pos(300, 300));

		list<Pos>::iterator it = lt1.begin();
		while (it != lt1.end())
		{
			//cout << (*it)._row << ":" << (*it)._col << endl;
			// 为了可读性,省略了一个->
			cout << it->_row << ":" << it->_col << endl;
			//cout << it->->_row << ":" << it->->_col << endl;
			cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;

			++it;
		}
		cout << endl;
	}


list包含在头文件

因为list类是一种模板,不建议list内部内容函数声明定义分开来写,所以都包含在同一个文件当中

#pragma once
#include<assert.h>

namespace bit
{
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;

		T _data;

		ListNode(const T& data = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;
		Node* _node;

		ListIterator(Node* node)
			:_node(node)
		{}

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

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

		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		Self& operator--(int)
		{
			Self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

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

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

	//template<class T>
	//class ListConstIterator
	//{
	//	typedef ListNode<T> Node;
	//	typedef ListConstIterator<T> Self;

	//	Node* _node;
	//public:
	//	ListConstIterator(Node* node)
	//		:_node(node)
	//	{}

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

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

	//	Self operator++(int)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_next;

	//		return tmp;
	//	}

	//	Self& operator--(int)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_prev;

	//		return tmp;
	//	}

	//	//*it
	//	const T& operator*()
	//	{
	//		return _node->_data;
	//	}

	//	const T* operator->()
	//	{
	//		return &_node->_data;
	//	}

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

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

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		// 不符合迭代器的行为,无法遍历
		//typedef Node* iterator;
		//typedef ListIterator<T> iterator;
		//typedef ListConstIterator<T> const_iterator;

		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		iterator begin()
		{
			//iterator it(_head->_next);
			//return it;
			return iterator(_head->_next);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		list()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}

		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;*/

			insert(end(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

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

		// 没有iterator失效
		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);
		}

		// erase 后 pos失效了,pos指向节点被释放了
		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

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

			delete cur;

			return iterator(next);
		}

	private:
		Node* _head;
	};

	void Func(const list<int>& lt)
	{
		// const iterator const 迭代器不能普通迭代器前面加const修饰
		// const 迭代器目标本身可以修改,指向的内容不能修改 类似const T* p 
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			// 指向的内容不能修改
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_list1()
	{
		list<int> lt1;

		// 按需实例化(不调用就不实例化这个成员函数)
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);

		Func(lt1);

		//ListIterator<int> it = lt1.begin();
		list<int>::iterator it = lt1.begin();
		while (it != lt1.end())
		{
			*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	struct Pos
	{
		int _row;
		int _col;

		Pos(int row = 0, int col = 0)
			:_row(row)
			,_col(col)
		{}
	};

	

	void test_list2()
	{
		list<Pos> lt1;
		lt1.push_back(Pos(100, 100));
		lt1.push_back(Pos(200, 200));
		lt1.push_back(Pos(300, 300));

		list<Pos>::iterator it = lt1.begin();
		while (it != lt1.end())
		{
			//cout << (*it)._row << ":" << (*it)._col << endl;
			// 为了可读性,省略了一个->
			cout << it->_row << ":" << it->_col << endl;
			//cout << it->->_row << ":" << it->->_col << endl;
			cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;

			++it;
		}
		cout << endl;
	}


	void test_list4()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);

		Func(lt1);

		lt1.push_front(10);
		lt1.push_front(20);
		lt1.push_front(30);

		Func(lt1);

		lt1.pop_front();
		lt1.pop_front();
		Func(lt1);

		lt1.pop_back();
		lt1.pop_back();
		Func(lt1);

		lt1.pop_back();
		lt1.pop_back();
		lt1.pop_back();
		lt1.pop_back();
		//lt1.pop_back();
		Func(lt1);
	}
}


 部分摘自— list模拟实现

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

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

相关文章

Windows【工具 06】mklink创建符号链接和硬链接(实现文件夹不同磁盘存储)

mklink创建符号链接和硬链接 1.创建符号链接1.1 目录符号链接1.2 文件符号链接 2.创建硬链接3.区别3.1 符号链接&#xff08;Symbolic Link&#xff09;3.2 硬链接&#xff08;Hard Link&#xff09; mklink是Windows中用于创建符号链接&#xff08;symbolic links&#xff09;…

数据库管理哪家强?Devart VS Navicat 360°全方位对比解析

今天我们向大家推荐的是两个开发环节的主流数据库管理品牌&#xff0c;那么你知道这两款数据库管理软件品牌与 数据库引擎配套的管理软件有什么区别吗&#xff1f;小编这就360全方位为您解答&#xff1a; ★ 品牌介绍 Devart&#xff1a;拥有超过20年的经验&#xff0c;利用最…

docker目录挂载失败:Check if the specified host path exists and is the expected type

docker目录挂载失败&#xff1a;Check if the specified host path exists and is the expected type docker目录挂载命令&#xff0c;其目的是为了达到修改linux上的文件同步到容器上&#xff0c;从而实现修改容器的配置文件。 在docker目录挂载或启动容器时报错&#xff0c…

若依前后端分离Spring Security新增手机号登录

备忘贴 转自&#xff1a;【若依RuoYi短信验证码登录】汇总_数据库_z_xiao_qiang-RuoYi 若依 配置Security: 按照Security的流程图可知&#xff0c;实现多种方式登录&#xff0c;只需要重写三个主要的组件&#xff0c;第一个用户认证处理过滤器&#xff0c;第二个用户认证tok…

【Git 版本管理】合并 + 变更,看懂Git

看懂 Git 合并操作分离 HEAD分离 HEAD 测试 相对引用(^ || ~)操作符 ^相对引用 ^ 测试操作符 ~相对引用 ~ 测试 撤销变更Git ResetGit Revert撤销变更 测试 整理提交记录Git Cherry-pick测试 交互式 rebase交互式 rebase 测试 合并操作 关键字&#xff1a;commit、branch、merg…

自媒体必用的50 个最佳 ChatGPT 社交媒体帖子提示prompt通用模板教程

在这个信息爆炸的时代&#xff0c;社交媒体已经成为我们生活中不可或缺的一部分。无论是品牌宣传、个人展示&#xff0c;还是日常交流&#xff0c;我们都离不开它。然而&#xff0c;要在众多信息中脱颖而出&#xff0c;吸引大家的关注并不容易。这时候&#xff0c;ChatGPT这样的…

DBeaver添加DM8驱动(maven下载和jar包下载配置)

DBeaver 24.0.3添加DM8驱动 下载DBeaver下载DM达梦驱动下载 安装配置使用自带Dameng自行添加达梦驱动 因为最近公司项目有信创要求&#xff0c;所以下载了达梦数据库。使用自带的达梦管理工具不是很方便&#xff0c;于是换了DBeaver。 哼哧哼哧安装好后&#xff0c;创建数据库连…

win11+vmware16.0+Ubuntu22.04+开机蓝屏

总结 本机系统 vm虚拟机下载 参考链接 1. 小白必看的Ubuntu20.04安装教程&#xff08;图文讲解&#xff09; 2. 软件目录【火星】——VM下载 3. Win11使用VMware15/16启动虚拟机直接蓝屏的爬坑记录 VMware16.0

MySQL(十二) Connector/C

首先可以在mysql的官网下载对应的库文件&#xff0c;这里就不演示了 1. 测试 通过 mysql_get_client_info() 函数&#xff0c;来验证我们的引入是否成功 #include <iostream> #include <mysql/mysql.h>int main() {std::cout << "mysql version:&quo…

帕友的锻炼小建议,助您重拾健康与活力

帕金森病&#xff0c;作为一种常见的神经系统退行性疾病&#xff0c;给患者的生活带来了诸多困扰。然而&#xff0c;通过科学的锻炼方法&#xff0c;我们可以有效缓解病情&#xff0c;提高生活质量。 一、有氧运动&#xff1a;提升心肺功能 对于帕金森病患者来说&#xff0c;适…

CentOS 7基础操作01_安装CentOS 7操作系统

1、实验环境 因为 Windows图形界面占用系统资源较高,所以公司准备将面向互联网的网站,数据库等重要应用基于Linux平台部署&#xff0c;并计划于近期将服务器安装开源免费的 CentOS 系统。进行前期准备工作时,需要公司的系统管理员尽快掌握 CentOS 系统的安装过程 2、需要描述 …

方差和标准差的区别

标准差和方差都是用来衡量随机变量的离散程度的统计量&#xff0c;它们之间有以下区别&#xff1a; 方差&#xff08;Variance&#xff09;&#xff1a; 方差是衡量随机变量离其均值的离散程度的度量。它是各个数据与其平均值之差的平方和的平均值。方差的公式为&#xff1a;…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt + uniapp 驾校预约平台 的设计与实现

一.项目介绍 系统角色&#xff1a;管理员、教练、学员 小程序(仅限于学员注册、登录)&#xff1a; 查看管理员发布的公告信息 查看管理员发布的驾校信息 查看所有教练信息、预约(需教练审核)、评论、收藏喜欢的教练 查看管理员发布的考试信息、预约考试(需管理…

javaweb基础之Ajax请求

大家好&#xff0c;这里是教授.F 目录 介绍&#xff1a; 原理图&#xff1a; 原生Ajax&#xff1a; JQuery的Ajax&#xff1a; 介绍&#xff1a; 是一种浏览器异步发起请求&#xff08;指定发那些数据&#xff09;&#xff0c;局部更新页面的技术 原理图&#xff1a; 原生…

MySQL -- SQL笔试题相关

1.银行代缴花费bank_bill 字段名描述serno流水号date交易日期accno账号name姓名amount金额brno缴费网点 serno: 一个 BIGINT UNSIGNED 类型的列&#xff0c;作为主键&#xff0c;且不为空。该列是自动增量的&#xff0c;每次插入新行时&#xff0c;都会自动递增生成一个唯一的…

【SQL学习进阶】从入门到高级应用(七)

文章目录 ✨数据处理函数✨if函数✨cast函数✨加密函数 ✨分组函数✨max✨min✨avg✨sum✨count✨分组函数组合使用✨分组函数注意事项 ✨分组查询✨group by✨having✨组内排序 ✨总结单表的DQL语句 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f495;欢迎来到我…

3、css3 手写nav导航条(互相学习)

效果例图&#xff1a; 1、首先呈现的是html代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

Optional类

一、概述 泛型类、java8引进的、java.util包里 二、作用 解决空指针异常带来的不便 三、做法 将对象封装为一个Optional对象&#xff0c;如果封装的对象为空&#xff08;即该对象不存在&#xff09;&#xff0c;可以使用默认值和或者执行默认操作 四、方法 1、empty() 创…

2024年艺术鉴赏与科学教育国际会议(ICAASE 2024)

2024年艺术鉴赏与科学教育国际会议 2024 International Conference on Art Appreciation and Science Education 【1】会议简介 2024年艺术鉴赏与科学教育国际会议是一场集艺术、科学和教育于一体的国际性学术盛会。本次会议旨在推动艺术鉴赏与科学教育领域的深入交流与合作&am…

通过 CXCR4 靶向纳米线修补细胞表面以抑制癌症转移

引用信息 文 章&#xff1a;Cell surface patching via CXCR4-targeted nanothreads for cancer metastasis inhibition. 期 刊&#xff1a;Nature Communications&#xff08;影响因子&#xff1a;16.6&#xff09; 发表时间&#xff1a;2024/3/29 作 者&#xff1…