STL---list

news2024/11/23 12:01:47

目录

1. list的介绍及使用

1.1 list的介绍

1.2 list的使用注意事项

2.list接口介绍及模拟实现

2.1构造​编辑

2.2容量

2.3修改

3.list迭代器

4.迭代器失效

5.模拟实现

6.vector和list的区别


1. list的介绍及使用

1.1 list的介绍

list的文档介绍

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

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

3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

1.2 list的使用注意事项

1.list不支持随机访问,不能使用[ ]来访问元素。

2.list.sort()使用的是归并排序,默认为升序,如果需要排降序

// 降序
list<int> lt;
greater<int> it;
lt.sort(it);

//匿名对象
lt.sort(greater<int> ());

但是使用list的sort排序效率不是很高,在处理数据量较多的情况下,可以将数据拷贝到vector中,再使用vector.sort(),再将数据拷贝回去。

list<int> lt;

vector<int> v(lt.begin(), lt.end());
sort(v.begin(), v.end());
lt.assign(v.begin(), v.end());

2.list接口介绍及模拟实现

2.1构造

void empty_init()
{
	head = new Node;
	head->next = head;
	head->prev = head;

	_size = 0;
}

list()
{
	empty_init();
}

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

2.2容量

bool empty()
{
	return head->next == head;
}

size_t size()
{
	return _size;
}

2.3修改

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

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

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

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

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

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = cur;
	cur->prev = newnode;

	++_size;
}

iterator erase(iterator pos)
{
	Node* cur = pos._node;
	Node* prev = cur->prev;
	Node* next = cur->next;

	prev->next = next;
	next->prev = prev;

	delete cur;
	--_size;

	return next;
}

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

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

3.list迭代器

与vector和string不同,list的迭代器需要自己实现,list迭代器可以理解为一个指针,指向list中的某个结点,而链表的每个结点在物理空间上并不连续,所以当迭代器++的时候,并不能直接到下一个结点的位置,并且*的时候,并不知道访问的是链表中的哪个数据,所以我们需要自己实现,将迭代器进行封装。

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

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

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

	self& operator++()
	{
		_node = _node->next;
		return *this;
	}

	self& operator--()
	{
		_node = _node->prev;
		return *this;
	}

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

需要注意的是,类模板参数有三个,是为了同时能实现迭代器和const迭代器,在list类中直接传不同的参数即可。

4.迭代器失效

此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array+sizeof(array)/sizeof(array[0]));

    auto it = l.begin();
    while (it != l.end())
    {
        // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
        l.erase(it);
        ++it;
    }
}

//修改

void TestListIterator()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array+sizeof(array)/sizeof(array[0]));

    auto it = l.begin();
    while (it != l.end())
    {
        it = l.erase(it);
    }
}

5.模拟实现

namespace cola
{
	template<class T>
	struct _list_node
	{
		T date;
		_list_node<T>* next;
		_list_node<T>* prev;

		_list_node(const T& value = T())
			: date(value)
			, next(nullptr)
			, prev(nullptr)
		{}
	};

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

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

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

		self& operator++()
		{
			_node = _node->next;
			return *this;
		}

		self& operator--()
		{	
			_node = _node->prev;
			return *this;
		}

		self operator++(int)
		{
			self temp(*this);
			_node = _node->next;
			return temp;
		}

		self operator--(int)
		{
			self temp(*this);
			_node = _node->prev;
			return temp;
		}

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

		bool operator==(const self& lt)
		{
			return _node == lt._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;
		}

		void empty_init()
		{
			head = new Node;
			head->next = head;
			head->prev = head;

			_size = 0;
		}

		list()
		{
			empty_init();
		}

		~list()
		{
			clear();
			delete head;
		}

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

		list& operator=(const list& lt)
		{
			if (head != lt.head)
			{
				clear();
				for (auto e : lt)
				{
					push_back(e);
				}
			}

			return *this;
		}

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

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

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

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

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

			prev->next = newnode;
			newnode->prev = prev;
			newnode->next = cur;
			cur->prev = newnode;

			++_size;
		}

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->prev;
			Node* next = cur->next;

			prev->next = next;
			next->prev = prev;

			delete cur;
			--_size;

			return next;
		}

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

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

		bool empty()
		{
			return head->next == head;
		}

		size_t size()
		{
			return _size;
		}

	private:
		Node* head;
		size_t _size;
	};

    
    //打印函数(适用于任何容器)
	template<typename Container>
	void print_container(const Container& lt)
	{
		typename Container::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}

		cout << endl;
	}
}

6.vector和list的区别

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

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

相关文章

第十四课:采用 Qt 开发翻页/分页/多页窗体组件

功能描述&#xff1a;采用 Qt 开发一个翻页/分页/多页的窗体组件&#xff0c;封装为 QWidget 的子类&#xff0c;在你的应用程序中可直接使用。 一、最终演示效果 本次制作的翻页/分页/多页窗体组件是基于 Qt 开发&#xff0c;整个程序封装成 PageWidget 类&#xff0c;继承于…

Mac桌面小部件:Widgetter

Widgetter是一个基于Python的Web应用程序&#xff0c;用于创建和管理小部件的在线平台。该平台可以让用户轻松地创建和定制各种小部件&#xff0c;包括时钟、计算器、天气预报、地图等等。Widgetter还提供了一套易于使用的工具和界面&#xff0c;供用户进行小部件的编辑和布局。…

vue3 基础知识 (组件之间的通信 and vuex) 02

侬好哇 &#xff01;&#x1f60d; 文章目录 一、组件的通信 &#xff08;父传子&#xff09;二、非 Prop 的Attribute (属性&#xff09;三、组件的通信 &#xff08;子传父&#xff09;四、非父子组件的相互通信&#xff08;Provide/Inject&#xff09;五、非父子组件的相互通…

FPGA使用MIG调用SODIMM内存条接口教程,提供vivado工程源码和技术支持

目录 1、前言免责声明 2、SODIMM内存条简介3、设计思路框架视频输入视频缓存MIG配置调用SODIMM内存条VGA时序视频输出 4、vivado工程详解5、上板调试验证6、福利&#xff1a;工程代码的获取 1、前言 FPGA应用中&#xff0c;数据缓存是一大重点&#xff0c;不管是图像处理还是A…

蓝牙防丢器(附HS6621芯片选型)

在繁忙的生活中&#xff0c;我们往往会因为疏忽而丢失贵重物品&#xff0c;如钱包、钥匙、手机等&#xff0c;给生活带来不小的麻烦。然而&#xff0c;现代科技正为我们提供一种聪明的解决方案——蓝牙防丢器。这款小巧智能的装置不仅保护您的财物&#xff0c;还为您的生活带来…

无涯教程-PHP - sql_regcase()函数

sql_regcase() - 语法 string sql_regcase (string string) 可以将sql_regcase()函数视为实用程序函数&#xff0c;它将输入参数字符串中的每个字符转换为包含两个字符的带括号的表达式。 sql_regcase() - 返回值 返回带括号的表达式字符串以及转换后的字符。 sql_regcase…

隧道vs免费爬虫ip:为何要选择隧道爬虫ip?

在网络爬虫的世界中&#xff0c;爬虫ip是一项关键技术&#xff0c;它可以帮助我们隐藏身份、突破限制、提高抓取效率。但是&#xff0c;在选择爬虫ip时&#xff0c;我们常常会面对隧道爬虫ip和免费爬虫ip之间的抉择。在本文中&#xff0c;我们将探讨隧道爬虫ip相对于免费爬虫ip…

再谈一下DDD中的聚合设计

何为聚合 在领域模型中&#xff0c;一些实体或者值对象具有强而有力的业务关联关系&#xff0c;于是这些对象就组成了一个聚合&#xff0c;聚合内部的业务实体之间必须保证状态一致性。从技术角度来看&#xff0c;聚合是数据修改与持久化的基本单元&#xff0c;聚合内数据修改…

docker实践作业

1.安装docker服务&#xff0c;配置镜像加速器 2.下载系统镜像&#xff08;Ubuntu、 centos&#xff09; 3.基于下载的镜像创建两个容器 &#xff08;容器名一个为自己名字全拼&#xff0c;一个为首名字字母&#xff09; 4.容器的启动、 停止及重启操作 5.怎么查看正在运行的容器…

SpeedBI数据可视化工具:浏览器上做分析

SpeedBI数据分析云是一种在浏览器上进行数据可视化分析的工具&#xff0c;它能够将数据以可视化的形式呈现出来&#xff0c;并支持多种数据源和图表类型。 所有操作&#xff0c;均在浏览器上进行 在浏览器中打开SpeedBI数据分析云官网&#xff0c;点击【免费使用】进入&#…

Faster RCNN网络数据流总结

前言 在学习Faster RCNN时&#xff0c;看了许多别人写的博客。看了以后&#xff0c;对Faster RCNN整理有了一个大概的了解&#xff0c;但是对训练时网络内部的数据流还不是很清楚&#xff0c;所以在结合这个版本的faster rcnn代码情况下&#xff0c;对网络数据流进行总结。以便…

生成式AI将催生出大量新的就业机会,倒逼14亿员工学习掌握新技能

尽管IBM的研报指出生成式AI的普及将为未来三年内的14亿劳动力带来必须学习和掌握新技能的挑战&#xff0c;但它也强调这股浪潮不会引发失业潮&#xff0c;相反&#xff0c;它将催生出大量新的就业机会。生成式AI被认为将在各种商业模式中发挥重要作用&#xff0c;为企业提供了利…

STM32CubeMx配置HAL库PWM

PWM简介 PWM(Pulse Width Modulation)是脉冲宽度调制的缩写&#xff0c;是一种利用微处理器的数字输出来对模拟电路进行控制的技术。PWM的原理是 通过调节占空比来调节脉冲宽度&#xff0c;从而改变输出电压的大小。波形图如下 PWM的两个重要参数为频率和占空比。频率是周期…

【最新附安装包】UG NX2023安装教程(CAD/CAM)

软件下载 软件&#xff1a;UG NX版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;10.78G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU3.0GHz 内存8G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.c…

(动态规划) 剑指 Offer 10- II. 青蛙跳台阶问题 ——【Leetcode每日一题】

❓剑指 Offer 10- II. 青蛙跳台阶问题 难度&#xff1a;简单 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如计算初始结果为&#xff1a;1…

2023企业数智化转型的正确打开方式是什么?他这样说(三)

哈喽~又见面了大家&#xff01;上两期我们说到了数据在数智化转型中的重要性以及监控在数智化转型中的角色&#xff0c;戳这里↓↓↓&#xff0c;一键回放精彩内容 2023企业数智化转型的正确打开方式是什么&#xff1f;他这样说&#xff08;一&#xff09;https://mp.csdn.net…

MacBook 上的 Asahi Linux 将基于 Fedora Linux

MacBook 上的 Asahi Linux 将基于 Fedora Linux Asahi Linux 旨在将 Linux 移植到使用 M1、M2 等苹果硅芯片的 Mac 电脑上。它最初是基于 Arch Linux 的软件包构建的&#xff0c;采用 Arch Linux ARM&#xff0c;添加了自己的覆盖包资源库&#xff0c;并将所有的集成工作打包到…

支持4KHz回报的电竞鼠标,游戏操作更精准,雷柏VT950Pro上手

雷柏这两年推出的V系列外设很受还原&#xff0c;配合新近推出的4K电竞游戏技术&#xff0c;非常适合在高分辨率的显示器上玩游戏。目前很多雷柏经典的鼠标型号&#xff0c;也都出了新版本&#xff0c;设计更加炫酷&#xff0c;还可以支持4K了&#xff0c;配上一只4K接收器就可以…

elastic-job源码- job自动装配

版本&#xff1a;3.1.0-SNAPSHOT git地址&#xff1a;GitHub - apache/shardingsphere-elasticjob: Distributed scheduled job framework Maven 坐标 1 2 3 4 5 <dependency> <groupId>org.apache.shardingsphere.elasticjob</groupId> <artif…

vite+vue3使用@路径,报错处理

报错原因&#xff1a;未配置 符号为指定路径别名&#xff0c;直接使用导致 处理方法&#xff1a; 安装path模块&#xff1a; npm install --save-dev types/node修改vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import path from…