【C++】list类(使用方法和模拟实现)

一、标准库中的list类

1.1 list类介绍

1.2 list的常用接口

1.2.1 常用的构造函数

1.2.2 容量操作接口

(1)size

(2)empty

(3)resize

1.2.3 访问和遍历

(1)迭代器

(2)反向迭代器

(3)back

(4)front

1.2.4 list的增删查改

(1)push_front

(2)pop_front

(3)push_back

(4)pop_back

(5)find

(6)insert

(7)erase

(8)swap

(9)assign

(10)clear

1.2.5 list的顺序修改接口

(1)sort

(2)reverse

二、模拟实现list类


一、标准库中的list类

1.1 list类介绍

  • list的底层是一个带哨兵位的双向循环链表结构
  • 对比forward_list的单链表结构,list的迭代器是一个双向迭代器
  • 与vector等顺序结构的容器相比,list在任意位置进行插入删除的效率更好,但是不支持任意位置的随机访问

list是一个模板类,在使用的时候我们需要给出元素的类型

使用list类时,需要包含头文件<list>

1.2 list的常用接口

1.2.1 常用的构造函数

list();

list类的默认构造函数,构造一个空链表

例如:

list(size_type n, const value_type& val =  value_type());

构造一个list对象并用n个val初始化

例如:

list(const list& x); 

list类的拷贝构造函数

例如:

Template<class InputIterator>

list(InputIterator first, InputIterator last);

使用迭代器进行初始化构造

例如:

1.2.2 容量操作接口

(1)size

size_type size() const;

获取链表节点个数

例如:

(2)empty

bool empty() const;

判断链表是否为空

例如:

(3)resize

void resize(size_type n, value_type val = value_type());

缩减或增加节点个数为n

如果没有给出val,就用0作为元素

例如:

1.2.3 访问和遍历

(1)迭代器

iterator begin();

const_iterator begin() const;

iterator end();

const_iterator end() const;

迭代器,用于获取链表中第一个节点的位置和最后一个节点的下一个位置(即哨兵位)

例如:

(2)反向迭代器

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

反向迭代器,rbegin获取容器中最后一个节点的位置,rend获取容器中哨兵位的位置

例如:

需要注意,反向迭代器rit也要用++而不是--

(3)back

reference back();

const_reference back() const;

返回链表中最后一个节点存储的数据的引用

例如:

(4)front

reference front();

const_reference front() const;

返回链表中第一个节点存储的数据的引用

例如:

1.2.4 list的增删查改

(1)push_front

void push_front(const value_type& val);

从链表头部插入一个元素

例如:

(2)pop_front

void pop_front();

从链表头部删除一个元素

例如:

(3)push_back

void push_back(const value_type& val);

从链表尾部插入一个元素

例如:

(4)pop_back

void pop_back();

从链表尾部删除一个元素

例如:

(5)find

template <class InputIterator, class T>

InputIterator find(InputIterator first, InputIterator last, const T& val);

在两个迭代器区间寻找val并返回其所在处的迭代器

例如:

需要注意的是,该函数并非list的成员函数,是标准库中的函数,多个容器共用该find函数

可以看到,我们可以直接对list的迭代器进行解引用并修改其内容,但是迭代器不应该指向节点吗?为什么对其解引用能修改数据呢?

实际上list的迭代器并不是用原生态指针进行模拟实现的,需要进行底层的封装,这里会在list的模拟实现的源代码中体现。

(6)insert

iterator insert(iterator position, const value_type& val);

void insert(iterator position, size_type n, const value_type& val);

————————————————————————————————————————

template<class InputIterator>

void insert(iterator position, InputIterator first, InputIterator last);

在position位置的前面插入一个或多个元素

例如:

细心的读者可能已经发现了,list的insert操作是不会导致迭代器失效的,因为pos指向的节点不变,相对位置也不变。

但是list的删除操作一定会导致迭代器失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

(7)erase

iterator erase(iterator position);

iterator erase(iterator first, iterator last);

删除position位置的元素或者 [first,last) 区间的所有元素

例如:

(8)swap

void swap(vector& x);

交换两个list对象

例如:

(9)assign

template <class InputIterator>

void assign(InputIterator first, InputIterator last);

void assign(size_type n, const value_type& val);

为list指定新内容,替换其当前内容并修改节点个数

例如:

(10)clear

void clear();

删除链表所有节点

例如:

1.2.5 list的顺序修改接口

(1)sort

void sort();

template<class Compare>

void sort(Compare comp);

list由于结构的特殊性,是无法使用标准库中的sort函数的,因为迭代器无法进行相减的操作

并且在C++文档中,我们可以看到标准库中的sort也限定了迭代器的类型必须是可以进行随机访问(RandomAccess)的

迭代器有几个功能分类:

  • 单向迭代器,只能进行++
  • 双向迭代器,可以++和--
  • 随机迭代器,可以++,--,+和-

但是list的sort函数也是没什么必要的,因为直接对链表排序的效率比拷贝到vector进行排序再拷贝回链表要慢的多

(2)reverse

void reverse();

对链表进行逆置

例如:


二、模拟实现list类

学习了list类中各种常用接口的用法后,我们就可以开始自己手撕一个自己的list类了

为了不和标准库中的list类冲突,我们可以开一个自己的命名空间

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

namespace Eristic
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& x = T()) //匿名对象作为缺省值,变为默认构造函数
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};
	 
	template<class T, class Ref, class Ptr> 
    //通过模板参数实现const迭代器和普通迭代器版本的复用
    //和list类中迭代器的typedef配合阅读会较好理解
	struct __list_iterator //重点:迭代器的底层封装
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;
        //node*本身不支持解引用等操作,对其进行封装就可以模仿原生态指针的行为

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

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

		Ptr operator->() //误区:在使用箭头运算符的时候,为什么只需要一个箭头而不是两个?
        //答:为了可读性,编译器会帮助我们省略一个箭头
		{
			 return &_node->_data;
		}

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

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

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
		//不需要写拷贝构造,因为我们用默认生成的进行浅拷贝就能完成需求
		//析构函数也不需要写,释放节点是list的事,与迭代器无关
	};

	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;
        //对应上面的多个模板参数

		void list_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			list_init();
		}

		list(int n, const T& x = T())
		{
			list_init();
			for (int i = 0; i < n; i++)
				push_back(x);
		}

		template<class iterator>
		list(iterator first, iterator last)
		{
			list_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		list(const list<T>& lt)
		{
			list_init();
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		list<T>& operator=(const list<T> lt)
		{
			swap(lt);
			return *this;
		}

		int size()
		{
			iterator it = begin();
			int count = 0;
			while (it != end())
			{
				++it;
				++count;
			}
			return count;
		}

		void resize(size_t n, const T& x = T())
		{
			if (n < size())
				while (n < size())
					pop_back();
			else if (n > size())
				while (n > size())
					push_back(x);
		}

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

		T& front()
		{
			return _head->_next->_data;
		}

		const T& front() const
		{
			return _head->_next->_data;
		}

		T& back()
		{
			return _head->_prev->_data;
		}

		const T& back() const
		{
			return _head->_prev->_data;
		}

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

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

			node* newnode = new node(x);

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

		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 iterator(next);
		}

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

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

	private:
		node* _head;
	};
}

如有错误,欢迎在评论区指出

完.

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

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

相关文章

DC-DC教程,真不错!

大家好&#xff0c;我是记得诚。 交流群读者分享了一个DC-DC的文档&#xff0c;内容还挺好&#xff0c;分享给大家。 文章原链接&#xff1a;DC-DC教程&#xff0c;真不错&#xff01;&#xff0c;可以获取完整的文档。 推荐阅读&#xff1a; 硬件工程师如何零基础入门&#…

数据结构:堆和二叉树遍历

堆的特征 1.堆是一个完全二叉树 2.堆分为大堆和小堆。大堆&#xff1a;左右节点都小于根节点 小堆&#xff1a;左右节点都大于根节点 堆的应用&#xff1a;堆排序&#xff0c;topk问题 堆排序 堆排序的思路&#xff1a; 1.升序排序&#xff0c;建小堆。堆顶就是这个堆最小…

【算法】五道大学生必备平价精致小众松弛感宝藏好题平替

【算法】五道大学生必备平价精致小众松弛感宝藏好题平替x ​ 刚学了Java就想用来写算法题的我&#xff1a; ​ 借着几道算法题&#xff0c;熟悉一下Java中Stack类&#xff0c;String类的用法。 925.长按键入 原题链接&#xff1a; 925. 长按键入 ​ 用来测试与练习String类自带…

php闭包应用

laravel 路由 bingTo 把路由URL映射到匿名回调函数上&#xff0c;框架会把匿名回调函数绑定到应用对象上&#xff0c;这样在匿名函数中就可以使用$this关键字引用重要的应用对象。Illuminate\Support\Traits\Macroable的__call方法。 自己写一个简单的demo: <?php <?…

遍历目录下的某个文件并删除

目录 需求 编写过程 演示 需求 大家在学习时可能会有一个自己的小目录&#xff0c;里面放着各种奇葩代码&#xff0c;有天突然发现&#xff0c;没有空间了&#xff0c;这时候发现遗留了很多的可执行文件&#xff0c;大大的浪费了我们的空间&#xff0c;但是由于层数深&#…

基于SSM的宠物领养平台的设计与实现

基于SSM的宠物领养平台的设计与实现 获取源码——》公主号&#xff1a;计算机专业毕设大全 获取源码——》公主号&#xff1a;计算机专业毕设大全

React腳手架已經創建好了,想使用Vite作為開發依賴

使用Vite作為開發依賴 安裝VITE配置VITE配置文件簡單的VITE配置項更改package.json中的scripts在根目錄中添加index.html現在可以瀏覽你的頁面了 安裝VITE 首先&#xff0c;在現有的React項目中安裝VITE npm install vite --save-dev || yarn add vite --dev配置VITE配置文件 …

JavaEE--小Demo--数据库建立

目录 实验准备 本次所要新建的文件 实验步骤 step1-demo.sql 1.在resources文件夹下新建demo.sql文件 2.打开此目录&#xff0c;并运行命令提示符 3.打开数据库mysql -uroot -p 4.创建数据库create database demo; 5.使用数据库use demo; 6.导入数据source demo.sql;…

Prometheus+Grafana 监控Tongweb7(by lqw)

文章目录 1.准备工作2.Tongweb7部署3.Prometheus部署4.上传jar包并配置Tongweb75.Prometheus配置6.安装和配置Grafana 1.准备工作 本次参考&#xff1a;Prometheus监控Tongweb容器 1.使用虚拟机ip&#xff1a;192.168.10.51&#xff08;tongweb&#xff09;&#xff0c;192.1…

算法系列--哈希表

&#x1f495;"白昼之光&#xff0c;岂知夜色之深。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;算法系列–哈希表 今天为大家带来的是算法系列--哈希表 1.两数之和 链接: https://leetcode.cn/problems/two-sum/submissions/515941642/ 分析…

《C++ Primer 第五版 中文版》第12章 动态内存【阅读笔记 + 个人思考】

《C Primer 第五版 中文版》第12章 动态内存【阅读笔记 个人思考】 12.1 动态内存与智能指针12.1.1 shared_ptr类 静态内存包括&#xff1a;初始化只读数据段&#xff0c;初始化读写数据段&#xff0c;未初始化数据和常量数据段。 详细在下面博客总结&#xff1a; Linux系统下…

吴恩达机器学习-可选实验室:Softmax函数

文章目录 CostTensorflow稀疏类别交叉熵或类别交叉熵祝贺 在这个实验室里&#xff0c;我们将探索softmax函数。当解决多类分类问题时&#xff0c;该函数用于Softmax回归和神经网络。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyl…

【数据结构】顺序表和链表详解顺序表和链表的实现

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.线性表 1.1 顺序表 1.1.1 概念及结构 1.1.2 静态顺序表 1.1.3 动态顺序表 1.2 链表 1.2.1 链表的概念及结构 1.2.2 链表…

力扣HOT100 - 1. 两数之和

解题思路&#xff1a; 解法一&#xff1a;暴力 class Solution {public int[] twoSum(int[] nums, int target) {int n nums.length;for (int i 0; i < n; i)for (int j i 1; j < n; j) {if (target nums[i] nums[j])return new int[] { i, j };}return new int[…

pcl利用kdtree计算点云密度

点云密度挺需要的,很多时候需要知道点云密度才能进行下一步 这里利用kdtree计算点云密度 代码 结果 这里单位写错了&#xff0c;抱歉

Personal Website

Personal Website Static Site Generators hexo hugo jekyll Documentation Site Generator gitbook vuepress vitepress docsify docute docusaurus Deployment 1. GitHub Pages 2. GitLab Pages 3. vercel 4. netlify Domain 域名注册 freessl 域名解析域名…

【数据结构与算法】Kruskal最小生成树

原理 算法实现 主要函数&#xff1a; 查并集&#xff1a; find 点 x 的祖先edge的比较大小函数kruskal函数 #include<iostream> #include<algorithm>using namespace std;struct Edge{int a,b,w;}edg[200010]; int p[200010]; int n,m;bool compareEdg(const Ed…

Redis 教程系列之Redis 安装(二)

Windows 下安装 下载地址:Releases tporadowski/redis GitHub。 Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C 盘,解压后,将文件夹重新命名为 redis。 打开文件夹,内容如下: 打开一个 cmd 窗口 使用 c…

Apache TinkerPop 与 Gremlin 快速介绍

TinkerPop &#xff0c;Gremlin TinkerPop 是一个 Apache 项目&#xff0c;它为图数据库提供了一个通用的图处理框架。Gremlin 是 TinkerPop 框架的一部分&#xff0c;它是一个图遍历语言&#xff0c;用于在图数据库中执行复杂的图遍历查询。 Apache TinkerPop Apache Tinker…

AI程序员革命:探析Devin的登场与编程未来

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…