list上

news2024/11/14 19:43:32

文章目录

初步了解list

面试题:为什么会有list?

答:为了解决vector的缺点

vector的缺点:

插入数据需要挪动数据;
插入数据需要增容;
注:
vector和string都是数组,在哪个位置插入数据,后面的数据是要往后挪动位置的;所以时间复杂度是o(n);
而list是链表,插入数据时o(1);

vector、list优点

vector支持下标随机访问;
list头插尾插不需要挪动数据、效率高;并且不需要增容

list结构

本质是带头双向循环链表;
列表种类有哪些?
答:带头、不带头;循环、不循环;单向、双向;这样2^3组合种;
image.png
只要一个容器支持迭代器,就可以用范围for遍历;

迭代器的分类

从支持的接口角度:
单向(forward_list)、双向(list)、随机(vector);
一般从使用场景分类:
正向、反向、正向const、方向const;

list的简单运用

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<list>

using namespace std;

void print_list(const list<int>& l)
{
	list<int>::const_iterator it = l.begin();

	while (it != l.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

}



void test_vector1()
{
	list<int> l;
	l.push_back(1);
	l.push_back(1);
	l.push_back(1);
	l.push_back(1);
	l.push_back(1);
	l.push_back(1);

	list<int>::iterator it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//拷贝
	list<int> l1(l);
	print_list(l1);
}


void test_vector2()
{
	//insert、erase
	//vector的迭代器失效insert、erase都会、list只用erase会迭代器失效

	list<int> l1;
	l1.push_back(1);
	l1.push_back(20);
	l1.push_back(3);
	l1.push_back(5);
	l1.push_back(8);
	l1.push_back(10);

	print_list(l1);

	list<int>::iterator pos = find(l1.begin(), l1.end(), 20);//现在pos就是begin()开始往后面找,里面数值为20的迭代器
	l1.insert(pos, 22);
	for (auto e : l1)
	{
		cout << e << " ";
		e++;
	}
	cout << endl;

	l1.erase(pos);
	l1.insert(pos,11);
	print_list(l1);


}


int main()
{
	test_vector2();
	return 0; 
}

insert、erase、迭代器失效(和vector的区别)

插在这个pos位置
image.png

erase

1、这个pos的所指向的仍是指向20的这个迭代器没有变化,因为他是列表;
2、如果是vector或者string那么在insert之后再用pos会发生迭代器失效,为什么?
答:迭代器失效:因为insert之后pos所指向的内容发生了改变,vector和string本质是一个数组中间插入一个数后面数的位置都要改变、pos这个迭代器指向的地址上的内容改变了就会报错,如果发生了增容、那更加错了、增容的话原本这个数组的地址就变了;
而list是列表,列表和数组(数据结构上的内容),列表是里面存了一个数值、然后还存了一个指向下一个列表的指针,所以插入值后pos指向的位置始终不变、只是插入的新的这个数据里面有个指针指向了pos;
image.png
3、erase的话、list和vector都会存在迭代器失效
erase,list是这个pos迭代器,本质就是pos这个指针指向的空间被销毁了;
vector道理相似;
image.png

class和struct

struct和class是一样的,只是struct默认是public,class默认是private;
struct内数据默认是public类型的,class内数据默认是private类型的;
struct变量放在栈上,而class变量放在堆上,因此struct变量会自动释放,而class变量需要手动释放;

list的迭代器

是另一个struct出来的,不是列表里面的next和prev指针;
image.png
image.png
list::iterator it = lt.begin()

为什么这个迭代器的构造函数不用深拷贝?

描述:这个迭代器就不用深拷贝了,用默认的浅拷贝就行了
image.png
答:这里直接让他两指向同一个空间,不会像vector、string要深拷贝、比如v1(v2),这个v1、v2浅拷贝的话就是他们内部指针指的位置都一样了,那就是v1释放空间v2也没了,但是迭代器用不到这个功能;

tmp 是一个在函数内部创建的局部变量,但它的引用被返回给调用者。当函数执行完毕或者离开 tmp 的作用域时,tmp 对象本身并不会被销毁,因为它的引用仍然存在于调用者的上下文中。
这意味着,即使超出了包含 tmp 的作用域,tmp 对象仍然存在,并且可以通过返回的引用进行访问。只有当调用者不再使用返回的引用时,tmp 对象才会被销毁。

模拟list

注:

delete和delete[]区别是前者释放单个对象、后者释放数组对象

具体代码

写一个列表的结构体

构造函数以及其中的成员函数

	template<class T>
	//struct__list_node,两个_这种取名方式一般表示一会这个会在别的里面用,在这里就表示,list类里面直接用的类外面定义的struct __list_code这个结构体
	struct __list_node
	{
		__list_node<T>* _next; // 别忘了这里要写<T>
		__list_node<T>* _prev;
		T _data;

		//列表需要构造函数来初始化
		__list_node(const T& x = T())//这个T(),指的是没有传值过来那就直接为0
			:_data(x)
			,_next(nullptr)  
			,_prev(nullptr)
		{}
	};

迭代器

主要用到list里面的两个指针,分别指向前后_next、_prev;

	//struct迭代器__list_iterator
	template<class T>
	struct __list_iterator
	{
		typedef __list_node<T> Node;
		Node* _node;

		//构造函数,struct怎么写构造函数、就是看这个里面成员函数有哪些
		__list_iterator(Node* node)
			:_node(node)
		{}

		//构造传的是里面的成员函数、拷贝是传的类或者说这个结构体
		//浅拷贝,这个是默认函数、不用写的
		__list_iterator(const __list_iterator<T>& it)
		{
			_node = it._node;
		}

		// operator*,读_node中的data,data的类型是T,所以函数返回值类型是T
		T& operator*()
		{
			return _node->_data;
		}


		//++
		__list_iterator<T>& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		__list_iterator<T> operator++(int) //后置++里面就放个int,这是设计出来的伪参数,用来区分前后置用的
		{
			__list_iterator<T> tmp(*this); //直接拷贝函数、这个不用写、因为是浅拷贝,深拷贝才要写
			_node = _node->_next; 
			//++(*this);
			return tmp;
		}

		//--
		__list_iterator<T>& operator--() 
		{
			_node = _node->_prev; //_node是指针
			return *this; //*this就是迭代器,this是指针指向迭代器
		}
		__list_iterator<T>& operator--(int) //后置++里面就放个int,这是设计出来的伪参数,用来区分前后置用的
		{
			__list_iterator<T> tmp(*this); //直接拷贝函数、这个不用写、因为是浅拷贝,深拷贝才要写
			//node = _node->_next;
			--(*this);
			return tmp;
		}




		//!=,两个迭代器之间不相等、迭代器里面的成员函数是_node,比的是这两个,就是他指向一个列表的地址,看这两个地址一样吗
		bool operator!=(const __list_iterator<T>& it)
		{
			return _node != it._node;
		}

		T* operator->()
		{
			return& _node->_data;//返回这个地址
			//指向这个地址,调用时第一个->返回其地址、第二个->返回地址上的值,迭代器做了优化,调用只需要一个->
		}


	};

class类

template<class T>
class list
{
	typedef __list_node<T> Node;
public:
	typedef __list_iterator<T> iterator; // 放public里面不然外面动不了T,就是用不了模板,私有的不能用

	//迭代器用节点的指针就行了
	iterator begin()
	{
		return iterator(_head->_next);
		//注意这里返回的是迭代器,不是_head头部指针、这个是list类里面的成员变量
		//现在是把头部指针_head传过去,然后迭代器调用他的拷贝函数;
		//这里面就是构造函数、因为传过去的是Node类型;
	}
	iterator end()
	{
		return iterator(_head);
	}

	//构造函数
	list()
	{
		_head = new Node;//new node //这里面不用写成Node[],是因为[]是数组用的,这个就里面就开辟一个指针指向的地址空间,[]是一个数组的空间
		_head->_next = _head;
		_head->_prev = _head;

	}

	void push_back(const T& x = T())
	{
		Node* tail = _head->_prev;
		Node* newnode = new Node(x);

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

private:
	Node* _head;
};


两个测试案例

	void test_list1()
	{
		list<int> lt;

		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

	}
	

	struct Date
	{
		int _year = 0;
		int _month = 1;
		int _day = 2;

	};

	void test_list2()
	{
		list<Date> lt;
		lt.push_back(Date());
		lt.push_back(Date());

		list<Date>::iterator it = lt.begin();//调用data,因为现在模板T就是date,传参传过去date是可以的
		while (it != lt.end())
		{
			cout << it->_year << " " << it->_month << " " << it->_day;
			++it;
		}
		cout << endl;
	}

报错

image.png
修改
image.png

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

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

相关文章

go 语言(九)----struct

定义一个结构体 type Book struct {title stringauth string }结构体使用 package mainimport "fmt"//定义一个结构体 type Book struct {title stringauth string }func main() {var book1 Bookbook1.title "Golang"book1.auth "zhang3"fmt…

PSoc62™开发板之i2c通信

实验目的 使用模拟i2c接口读取温湿度气压模块BME280数据 实验准备 PSoc62™开发板温湿度气压模块BME280公母头杜邦线 板载资源 本次实验是通过模拟i2c时序的方式来进行通信&#xff0c;理论上可以有非常多的方式配置i2c引脚&#xff0c;不像硬件i2c那样芯片出厂引脚已经固…

Apache Doris (六十七): DataX DorisWriter - (1) - 源码编译及集成

🏡 个人主页:IT贫道-CSDN博客 🚩 私聊博主:私聊博主加WX好友,获取更多资料哦~ 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录

GetShell的姿势

0x00 什么是WebShell 渗透测试工作的一个阶段性目标就是获取目标服务器的操作控制权限&#xff0c;于是WebShell便应运而生。Webshell中的WEB就是web服务&#xff0c;shell就是管理攻击者与操作系统之间的交互。Webshell被称为攻击者通过Web服务器端口对Web服务器有一定的操作权…

怎么移除WordPress后台工具栏的查看站点子菜单?如何改为一级菜单?

默认情况下&#xff0c;我们在WordPress后台想要访问前端网站&#xff0c;需要将鼠标移动到左上角的站点名称&#xff0c;然后点击下拉菜单中的“查看站点”才行&#xff0c;而且还不是新窗口打开。那么有没有办法将这个“查看站点”子菜单变成一级菜单并显示在顶部管理工具栏中…

数据分析-Pandas如何整合多张数据表

数据分析-Pandas如何整合多张数据表 数据表&#xff0c;时间序列数据在数据分析建模中很常见&#xff0c;例如天气预报&#xff0c;空气状态监测&#xff0c;股票交易等金融场景。数据分析过程中重新调整&#xff0c;重塑数据表是很重要的技巧&#xff0c;此处选择Titanic数据…

Qt拖拽组件与键盘事件

1.相关说明 1.设置widget或view的拖拽和放置模式函数setDragDropMode参数说明&#xff0c;NoDragDrop(无拖拽和放置)、DragOnly(只允许拖拽)、DropOnly(只允许放置)、DragDrop(允许拖拽和放置)、InternalMove(只移动不复制) 2.设置widget或view的放置动作函数setDefaultDropAct…

小周带你读论文-2之“草履虫都能看懂的Transformer老活儿新整“Attention is all you need(2)

书接前文&#xff1a;小周带你读论文-2之"草履虫都能看懂的Transformer老活儿新整"Attention is all you need(1) (qq.com) 上文书说到为什么我们要用casual-decoder架构&#xff0c;把Transformer的左边给省略了&#xff0c;于是得到下图这样的架构 上图是GPT-1的模…

leetcode2312卖木头块

其实不难&#xff0c;主要是你得一眼看出来这个问题缩小规模然后就可以用DP来做了 using ll long long; class Solution { public:long long sellingWood(int m, int n, vector<vector<int>>& prices) {ll f[m10][n10];ll p[m10][n10];memset(p,0,sizeof p)…

AI时代—ChatGPT-4.5的正确打开方式

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言4.5key价格泄漏ChatGPT4.0使用地址ChatGPT正确打开方式最新功能语音助手存档…

数据结构——Java实现栈和队列

一、栈 Stack 1.特点 &#xff08;1&#xff09;栈是一种线性数据结构 &#xff08;2&#xff09;规定只能从栈顶添加元素&#xff0c;从栈顶取出元素 &#xff08;3&#xff09;是一种先进后出的数据结构&#xff08;Last First Out&#xff09;LIFO 2.具体实现 Java中可…

win系统环境搭建(十二)——Windows系统下使用docker安装redis

windows环境搭建专栏&#x1f517;点击跳转 win系统环境搭建&#xff08;十二&#xff09;——Windows系统下使用docker安装redis 文章目录 win系统环境搭建&#xff08;十二&#xff09;——Windows系统下使用docker安装redis1.创建文件夹2.docker-compose.yaml配置文件3.red…

《WebKit 技术内幕》之四(1): 资源加载和网络栈

第四章 资源加载和网络栈 使用网络栈来下载网页和网页资源是渲染引擎工作的第一步 1.WebKit 资源加载机制 1.1 资源 网页本身就是一种资源、网页还需要依赖很多其他的资源(图片、视频) &#xff08;1&#xff09;HTML 支持的资源主要包括以下几种类型&#xff1a; HTML 页…

【动态规划】【C++算法】741摘樱桃

作者推荐 【动态规划】【数学】【C算法】18赛车 涉及知识点 动态规划 LeetCode741 摘樱桃 给你一个 n x n 的网格 grid &#xff0c;代表一块樱桃地&#xff0c;每个格子由以下三种数字的一种来表示&#xff1a; 0 表示这个格子是空的&#xff0c;所以你可以穿过它。 1 表…

Golang 搭建 WebSocket 应用(六) - 监控

我在上一篇文章中&#xff0c;提到了目前的认证方式存在一些问题&#xff0c;需要替换为一种更简单的认证方式。 但是最后发现&#xff0c;认证这个实在是没有办法简单化&#xff0c;认证本身又是另外一个不小的话题了&#xff0c;因此关于这一点先留个坑。 本文先讨论一下另外…

前端开发必备 HTML的常用标签(二)

目录 一、HTML语言 二、水平线标签 三、字体样式标签 四、注释和特殊符号 一、HTML语言 HTML&#xff08;Hypertext Markup Language&#xff09;是一种标记语言&#xff0c;用于创建网页的结构和内容。它由一系列的标签组成&#xff0c;这些标签定义了网页中各个元素的结…

商铺工厂119消防火灾SOS声光一键报警器平台联网

商铺工厂119消防火灾SOS声光一键报警器平台联网 1.设有火灾自动报警系统的建筑&#xff0c;宜选择符合相关现行国家技术标准的消防应急广播、火灾声光警报类产品&#xff0c;由火灾报警或消防联动控制器进行控制&#xff0c;在消防控制室应能一键启动全楼火灾声光警报或向全楼进…

阿里云腾讯七牛内容安全配置

一&#xff0c;阿里云 1&#xff0c;配置RAM角色权限 向RAM用户授权系统策略权限&#xff1a;AliyunYundunGreenWebFullAccess 2&#xff0c;内容安全控制台——授权访问OSS 不授权——会报错——no permission(not authorized about role AliyunCIPScanOSSRole)

Pytest插件“pytest-selenium” - 让自动化测试更简洁

在现代Web应用的开发中,自动化测试成为确保网站质量的重要手段之一。而Pytest插件 pytest-selenium 则为开发者提供了简单而强大的工具,以便于使用Python进行Web应用的自动化测试。本文将深入介绍 pytest-selenium 插件的基本用法和实际案例,助你轻松进入无忧的Web应用测试之…

LeetCode 9.回文数(python版)

需求 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 例如&#xff0c;121 是回文&#xff0…