C++---list常用接口和模拟实现

list---模拟实现

  • list的简介
  • list函数的使用
    • 构造函数
    • 迭代器的使用
    • list的capacity
    • list element access
    • list modifiers
  • list的模拟实现
    • 构造函数,拷贝构造函数和=
    • 迭代器
    • begin和end
    • insert和erase
    • clear和析构函数
  • 源码

list的简介

list是用双向带头联表实现的一个容器,双向联表中每个元素存在互不相关的独立节点中,再节点中通过指针指向其前一个元素和后一个元素,并且可以再常数范围内再任意位置进行插入和删除的序列式容器。

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
void ListTest2()
{
	std::list<int> a(4, 5);
	std::cout << "a的list" << ':';
	for (auto& e : a)
	{
		std::cout << e << ' ';
	}
	std::cout << std::endl;
	std::list<int> b;//Empty

	std::list<int> c(a.begin(), a.end());
	std::cout << "c的list" << ':';
	for (auto& e : c)
	{
		std::cout << e << ' ';
	}

}

在这里插入图片描述

迭代器的使用

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置
	std::list<int> a(4, 5);
	std::cout << "a的list" << ':';
	std::list<int>::iterator it = a.begin();
	while (it != a.end())
	{
		std::cout << *it << ' ';
		++it;
	}

在这里插入图片描述

list的capacity

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

list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

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中的有效元素

list的模拟实现

要想模拟实现list,首先我们可以定义一个节点类。为什么要定义一个节点类呢,想想数据结构中的双链表,每个元素都在独立的节点中,通过节点的前驱指针和后继指针指向前后位置。

list的模拟实现跟vector和string实现是不一样的,vector本质上是一个数组,数组本身就是一个迭代器,而list是一个个的节点,通过指针联系在一起,所以vector类不用对迭代器进行封装,可以直接使用,list不一样,节点++是什么是不清楚的,这个时候可以使用运算符重载,对++,–,*等进行重载。

	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;

		list_node(const T& val = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(val)
		{}
	};

再定义一个list类,再这个类中,完成其他函数的实现

	template<class T>
	class list
	{
	public:
	private:
		Node* _head;
		size_t _size;
	};

构造函数,拷贝构造函数和=

list的构造函数实现,跟双链表中的初始化是一样的,将next指针和prev指针都指向头节点,形成一个环。

		void EmptyInit()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

拷贝构造函数再拷贝之前,要先开一个头节点的空间,然后再头节点后面依次把值进行尾插即可。

		list(const list<T>& it)
		{
			EmptyInit();

			for (auto& e : it)
			{
				push_back(e);
			}
		}

赋值重载=和vector里面的写法是一样的。

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

			return *this;
		}

通过传参创建出一个临时变量,然后将其交换。

迭代器

要想实现list的迭代器,需要将原生态指针进行封装。

list是由一个个的节点组成,节点++,解引用,–,等操作是找不到对应的数据的,所以需要用自定义类型对指针进行封装,从而完成一系列操作。

迭代器有一个const类型的迭代器,有一个无const类型的,再实现一个无const类型的迭代器之后,把代码赋值粘贴,也可以改成带const类型的迭代器,但这样显然是代码冗余的,重复的太多,这个时候,那些大佬们就通过增加模板参数,来解决代码冗余,根据参数的类型,实列化出不同的类。

	
//typedef __list_iterator<T, T&, T*> iterator;
//typedef __list_iterator<T,const T&,const T*> const_iterator;
template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;
		//构造方法
		__list_iterator(Node* node)
			:_node(node)
		{}
        
		//对节点解引用,是找不到我们存储的数据的,因为节点里面存的是val,next和prev,重载的目是使其对迭代器解引用可以找到有效数据
		Ref operator*()
		{
			return _node->_val;
		}

		Ptr operator->()
		{
			return &(_node->val);
		}
		
        //指针++,到下一个位置,其实就是让节点向后移动
		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 *this;
		}
		
		bool operator !=(const self& it) const
		{
			return _node != it._node;
		}

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


	};

以上就是用自定义的类,对指针进行封装。

begin和end

对指针进行封装之后,就可以实现begin和end了。

begin只要返回头节点的下一个节点即可

end就是头节点

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

		iterator end()
		{
			return _head;
		}

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

		const_iterator end() const
		{
			return _head;
		}

insert和erase

有一个insert,就可以完成再任意位置插入。

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

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

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

			prev->_next = newnode;
			newnode->_next = cur;

			newnode->_prev = prev;
			cur->_prev = newnode;

			return newnode;
		}

先找到当前节点cur和当前节点的上一个节点prev,再new出一个要插入的节点newnode,使得cur和newnode进行双向连接,prev和newnode进行双向连接。


删除一个节点,只需要让当前节点的上一个节点和下一个节点完成双向连接即可。

		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;

			--_size;

			return next;
		}

clear和析构函数

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = earse(it);
			}

			_size = 0;
		}
		
		
		~list()
		{}

源码

#pragma once

#include <assert.h>
#include <algorithm>

namespace HaiFan
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;

		list_node(const T& val = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(val)
		{}
	};



	template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;

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

		T& operator*()
		{
			return _node->_val;
		}

		T* operator->()
		{
			return &(_node->val);
		}

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

		T operator++(int)
		{
			self tmp(*this);

			_node = _node->_next;

			return tmp;
		}

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

		T operator--(int)
		{
			self tmp(*this);
			_node = _node->prev;

			return *this;
		}

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

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


	};

	template<class T>
	class list
	{
		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 _head->_next;
		}

		iterator end()
		{
			return _head;
		}

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

		const_iterator end() const
		{
			return _head;
		}

		list()
		{
			EmptyInit();
		}

		list(const list<T>& it)
		{
			EmptyInit();

			for (auto& e : it)
			{
				push_back(e);
			}
		}

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

			return *this;
		}

		void swap(list<T>& it)
		{
			std::swap(_head, it._head);
			std::swap(_size, it._size);
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = earse(it);
			}

			_size = 0;
		}

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

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

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

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

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

			prev->_next = newnode;
			newnode->_next = cur;

			newnode->_prev = prev;
			cur->_prev = newnode;

			return newnode;
		}

		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;

			--_size;

			return next;
		}

		size_t size()
		{
			return _size;
		}

		void EmptyInit()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		~list()
		{}

	private:
		Node* _head;
		size_t _size;
	};
}

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

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

相关文章

【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证

目录 测试工作中常用到的测试桩mock能力 应用场景 简单测试桩 http.server扩展&#xff1a;一行命令实现一个静态文件服务器 性能优化&#xff1a;使用异步响应 异步响应 能优化&#xff1a;利用多核 gunicorn 安装 gunicorn 使用 gunicorn 启动服务 性能优化&#…

PHP 前后端分离,运行配置

H5 WEB目录:安装 yarn install、npm install &#xff08;依赖包&#xff09; 在电脑&#xff1a;安装nodejs Composer下载 &#xff1a;https://getcomposer.org/

Amazon Aurora Serverless v2 正式发布:针对要求苛刻的工作负载的即时扩展

我们非常兴奋地宣布&#xff0c;Amazon Aurora Serverless v2 现已面向 Aurora PostgreSQL 和 MySQL 正式发布。Aurora Serverless 是一种面向 Amazon Aurora 的按需自动扩展配置&#xff0c;可让您的数据库根据应用程序的需求扩展或缩减容量。 亚马逊云科技开发者社区为开发者…

SAP 集成以及PO异步接口调优

前言&#xff1a;目前国内的SAP相关的技术文档实在是少得可怜&#xff0c;PO相关的就更少了&#xff0c;基本上都是需要摸索&#xff0c;官方的技术专家很多时候的回复都是说了又似乎没说。。。 背景&#xff1a;由于目标系统接收数据缓慢或者是异步线程出现异常导致错误积压。…

动手学深度学习(一)预备知识

目录 一、数据操作 1. N维数组样例 2. 访问元素 3. 基础函数 &#xff08;1&#xff09; 创建一个行向量 &#xff08;2&#xff09;通过张量的shape属性来访问张量的形状和元素总数 &#xff08;3&#xff09;reshape()函数 &#xff08;4&#xff09;创建全0、全1、…

c语言基础知识帮助理解(函数递归详解)

"从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚和一个小和尚。有一天老和尚对小和尚说:“从前有座山.山里有座庙&#xff0c;庙里有个老和尚和一个小和尚&#xff0c;有一天老和尚对小和尚说&#xff1a;“从前有座山.山里有座庙&#xff0c;庙里有个老和尚…

微信小程序tab加列表demo

一、效果 代码复制即可使用&#xff0c;记得把图标替换成个人工程项目图片。 微信小程序开发经常会遇到各种各样的页面组合&#xff0c;本demo为list列表与tab组合&#xff0c;代码如下&#xff1a; 二、json代码 {"usingComponents": {},"navigationStyle&q…

Github Pages自定义域名

Github Pages自定义域名 当你想在网上发布内容时&#xff0c;配置Github Pages是一个很好的选择。如果你想要在自己的域名上发布&#xff0c;你可以使用Github Pages来创建自己的网站。本文将介绍如何使用Github Pages自定义域名。 这里呢先列出前置条件&#xff1a; 您的Gi…

【无公网IP】在公网环境下Windows远程桌面Ubuntu 18.04

【无公网IP】在公网环境下Windows远程桌面Ubuntu 18.04 文章目录 【无公网IP】在公网环境下Windows远程桌面Ubuntu 18.04一、 同个局域网内远程桌面Ubuntu1. 更新软件仓库2. 安装支持包3. 安装XFCE4桌面环境4. 安装XRDP5. 环境设置5.1 XFCE桌面配置5.2 在配置文件中&#xff0c…

防雷工程行业应用和施工工艺

防雷工程是指通过各种手段和措施&#xff0c;保护建筑物、设备和人员免受雷电侵害的技术。在我国&#xff0c;由于雷电活动频繁&#xff0c;防雷工程的重要性不言而喻。地凯科技将介绍防雷工程的基本知识、相关案例以及防雷器产品。 一、防雷工程的基本知识 雷电的危害 雷电…

浅谈下API初步认知

当我们谈论API&#xff0c;我们指的是应用程序接口&#xff08;Application Programming Interface&#xff09;。API允许不同的软件应用程序之间互相通信和交互。它定义了一组规定和协议&#xff0c;用于确定数据传输和请求的格式、方法和功能。 API的作用是在软件开发中提供一…

LeetCode--HOT100题(19)

目录 题目描述&#xff1a;54. 螺旋矩阵&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;54. 螺旋矩阵&#xff08;中等&#xff09; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 Le…

企业服务器中了Locked勒索病毒后怎么办,如何解决问题并提高防范意识

科学技术的发展给我们的生活带来了极大便利&#xff0c;但也为企业带来了安全威胁。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器中了locked后缀勒索病毒&#xff0c;计算机上的所有文件都被加密&#xff0c;无法被正常调取&#xff0c;严重影响了企业的正…

Linux第六章之vim与gcc使用

一、Linux编辑器-vim使用 vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行&#xff0c;也…

Kotlin基础(十):函数进阶

前言 本文主要讲解kotlin函数&#xff0c;之前系列文章中提到过函数&#xff0c;本文是kotlin函数的进阶内容。 Kotlin文章列表 Kotlin文章列表: 点击此处跳转查看 目录 1.1 函数基本用法 Kotlin 是一种现代的静态类型编程语言&#xff0c;它在函数的定义和使用上有一些特点…

2023年第四届“华数杯”数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; 最短时间生产计划模型 该模型出现在好几个竞赛赛题上&#x…

【心电图信号压缩】ECG信号压缩与通过三次样条近似重建的ECG信号压缩研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringCloudAlibaba之Nacos配置中心

第一步&#xff1a;引入jar包 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> 第二步&#xff1a;在resources下创建一个bootstrap.yml文档…

HTTP杂谈之Referer和Origin请求头再探

一 关于Referer和Origin的汇总 1) 知识是凌乱的,各位看官看个热闹即可2) 内容不断更新1、理解有盲区,需要及时纠正2、内容交叉有重复,需要适当删减3、扩展视野3) 以下内容都与Referer和Origin请求头有关联 nginx防盗链 HTTP杂谈之Referrer-Policy响应头 iframe标签referre…

Windows用户如何将cpolar内网穿透配置成后台服务,并开机自启动?

Windows用户如何将cpolar内网穿透配置成后台服务&#xff0c;并开机自启动&#xff1f; 文章目录 Windows用户如何将cpolar内网穿透配置成后台服务&#xff0c;并开机自启动&#xff1f;前置准备&#xff1a;VS Code下载后&#xff0c;默认安装即可VS CODE切换成中文语言 1. 将…