STL——list

news2024/11/18 6:36:54

一、list介绍及使用

1. list文档介绍

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

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

(3)list和forward_list非常相似。最主要的不同在于forward_list是单链表,只能朝前迭代。

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

(5)与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问。

2. list常用接口介绍

2.1 list的构造

2.2 list iterator的使用

2.3 list capacity

 

2.4 list 元素访问

2.5 list 元素修改

3. list迭代器失效问题

3.1迭代器失效

        迭代器失效,即迭代器所指向的节点无效,在list中即该节点被删除了。

        因为list的底层结构为带头节点的双向循环链表,因此在list中插入元素是不会导致list迭代器失效,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

3.2 list可能导致迭代器失效的操作

        pop_back、pop_front、erase、resize、assign……

3.3解决方法

        在所有可能导致迭代器失效的操作之后,在使用迭代器前对迭代器进行重新赋值。

二、list与vector的区别★

 注意:

        空间利用率上,只是绝大多数情况下vector比list高,因为list每个节点多了两个指针域,但并不绝对。

三、模拟实现list

1.实现接口

1.1私有成员

void CreateHead();//创建头节点
Node* _head;

1.2默认成员

//默认构造函数
list();
//n个value构造
list(size_t n, const T& value = T());
template<class Iterator>
//区间构造
list(Iterator first, Iterator last);
//拷贝构造
list(const list<T>& L);
//赋值运算符重载
list<T>& operator=(list<T> L)

//析构函数
~list();

1.3迭代器

注意:

        list迭代器不能使用原生态的指针,因为list空间不连续,不能对指针进行算数运算。所以需要自己模拟封装迭代器对应类

template<class T, class Ref, class Ptr>
struct ListIterator;
template<class Iterator>
struct ListReverseIterator;

iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator crbegin() const;
const_reverse_iterator crend() const;

1.4容量

size_t size() const ;
bool empty() ;
void resize(size_t newSize, const T& value = T()) ;

1.5元素访问

T& front();
const T& front() const;
T& back();
const T& back() const ;

1.6元素修改

void push_front(const T& value);
void pop_front();
void push_back(const T& value) ;
void pop_back();
iterator insert(iterator it, const T& value);
iterator erase(iterator it);
void clear();
void swap(list<T>& L) ;

2.代码实现

#include <iostream>
using namespace std;

namespace MyList {
	//链表节点
	template<class T>
	struct ListNode {
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _val;

		ListNode(const T& value = T())
			: _next(nullptr)
			, _prev(nullptr)
			, _val(value)
		{}
	};

	//注意:list迭代器不能使用原生态的指针
	//因为list空间不连续,不能对指针进行算数运算

	//模拟封装迭代器类
	template<class T, class Ref, class Ptr>
	struct ListIterator {
		typedef ListNode<T> Node;
		typedef Ref ItRef;
		typedef Ptr ItPtr;
		typedef ListIterator<T, Ref, Ptr> Self;
	public:
		//构造
		ListIterator(Node* node = nullptr)
			:_node(node)
		{}

		/*----------实现类似指针的操作----------*/
		Ref operator*() {
			return _node->_val;
		}
		Ptr operator->() {
			return &(operator*());
		}

		/*----------实现"++""--"----------*/
		Self& operator++() {
			_node = _node->_next;
			return *this;
		}
		Self operator++(int) {
			Self temp(*this);
			_node = _node->_next;
			return temp;
		}
		Self& operator--() {
			_node = _node->_prev;
			return *this;
		}
		Self& operator--(int) {
			Self temp(*this);
			_node = _node->_prev;
			return temp;
		}

		/*----------实现比较----------*/
		bool operator!=(const Self& s) const {
			return _node != s._node;
		}
		bool operator==(const Self& s) const {
			return _node == s._node;
		}

	public:
		Node* _node;
	};

	//模拟封装反向迭代器:内部封装正向迭代器即可
	template<class Iterator>
	struct ListReverseIterator {
		//因为静态成员变量也可以通过类名::静态成员名称的方式进行访问
		//直接写会编译报错,编译器不确定ItRef、ItPtr是静态成员变量还是类型
		//所以需要显示告诉编译器是typename
		typedef typename Iterator::ItRef Ref;
		typedef typename Iterator::ItPtr Ptr;
		typedef ListReverseIterator<Iterator> Self;
	public:
		ListReverseIterator(Iterator it)
			:_it(it)
		{}

		Ref operator*() {
			Iterator temp(_it);
			--temp;
			return *temp;
		}
		Ptr operator->() {
			return &(*operator*());
		}

		Self& operator++() {
			--_it;
			return *this;
		}
		Self operator++(int) {
			Self temp(*this);
			--_it;
			return temp;
		}
		Self& operator--() {
			++_it;
			return *this;
		}
		Self operator--(int) {
			Self temp(*this);
			++_it;
			return temp;
		}
		bool operator!=(const Self& rit) const {
			return _it != rit._it;
		}
		bool operator==(const Self& rit) const {
			return _it == rit._it;
		}
	public:
		Iterator _it;//正向迭代器
	};

	/*=========================================================================================*/

	template<class T>
	class list {
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;
		typedef ListReverseIterator<iterator> reverse_iterator;
		typedef ListReverseIterator<const_iterator> const_reverse_iterator;
	public:
		/*----------默认成员----------*/
		//默认构造函数
		list() {
			CreateHead();
		}
		//n个value构造
		list(int n, const T& value = T()) {
			CreateHead();
			for (int i = 0; i < n; ++i) { push_back(value); }
		}
		template<class Iterator>
		//区间构造
		list(Iterator first, Iterator last) {
			CreateHead();
			while (first != last) {
				push_back(*first);
				++first;
			}
		}
		//拷贝构造
		list(const list<T>& L) {
			CreateHead();
			auto it = L.cbegin();
			while (it != L.cend()) {
				push_back(*it);
				++it;
			}
		}
		//赋值运算符重载
		list<T>& operator=(list<T> L) {
			this->swap(L);
			return *this;
		}

		//析构函数
		~list() {
			clear();
			delete _head;
			_head = nullptr;
		}

		/*----------迭代器----------*/
		iterator begin() {
			return iterator(_head->_next);
		}
		iterator end() {
			return iterator(_head);
		}
		const_iterator cbegin() const {
			return const_iterator(_head->_next);
		}
		const_iterator cend() const {
			return const_iterator(_head);
		}
		reverse_iterator rbegin() {
			return reverse_iterator(end());
		}
		reverse_iterator rend() {
			return reverse_iterator(begin());
		}
		const_reverse_iterator crbegin() const {
			return const_reverse_iterator(cend());
		}
		const_reverse_iterator crend() const {
			return const_reverse_iterator(cbegin());
		}

		/*----------容量----------*/
		size_t size() const {
			Node* cur = _head->_next;
			size_t count = 0;
			while (cur != _head) {
				++count;
				cur = cur->_next;
			}
			return count;
		}
		bool empty() {
			return _head->_next == _head;
		}
		void resize(size_t newSize, const T& value = T()) {
			size_t oldSize = size();
			if (newSize < oldSize) {
				for (size_t i = oldSize; i > newSize; --i) { pop_back(); }
			}
			else {
				for (size_t i = oldSize; i < newSize; ++i) { push_back(value); }
			}
		}

		/*----------元素访问----------*/
		T& front() {
			return *begin();
		}
		const T& front() const {
			return *cbegin();
		}
		T& back() {
			return *(--end);
		}
		const T& back() const {
			return *(--cend);
		}

		/*----------元素修改----------*/
		void push_front(const T& value) {
			insert(begin(), value);
		}
		void pop_front() {
			erase(begin());
		}
		void push_back(const T& value) {
			insert(end(), value);
		}
		void pop_back() {
			erase(end()--);
		}
		iterator insert(iterator it, const T& value) {
			Node* pos = it._node;
			//插入方式,将新节点连接进去,再断开原链表
			Node* temp = new Node(value);
			//(1)连接
			temp->_next = pos;
			temp->_prev = pos->_prev;
			//(2)断开
			temp->_prev->_next = temp;
			temp->_next->_prev = temp;
			return iterator(temp);
		}
		iterator erase(iterator it) {
			if (it == end()) return end();
			Node* pos = it._node;
			Node* ret = pos->_next;

			pos->_prev->_next = pos->_next;
			pos->_next->_prev = pos->_prev;
			delete pos;
			return iterator(ret);
		}
		void clear() {
			auto it = begin();
			while (it != end()) {
				it = erase(it);//记得重新赋值,防止迭代器失效
			}
		}
		void swap(list<T>& L) {
			std::swap(_head, L._head);
		}

		/*----------私有成员----------*/
	private:
		void CreateHead() {//创建头节点
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}
		Node* _head;
	};
}

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

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

相关文章

【Java|golang】2335. 装满杯子需要的最短总时长

现有一台饮水机&#xff0c;可以制备冷水、温水和热水。每秒钟&#xff0c;可以装满 2 杯 不同 类型的水或者 1 杯任意类型的水。 给你一个下标从 0 开始、长度为 3 的整数数组 amount &#xff0c;其中 amount[0]、amount[1] 和 amount[2] 分别表示需要装满冷水、温水和热水的…

web期末复习 2023.02.11

文章目录Web 的概念Web 组成用户通过浏览器请求资源的过程:HTML 超文本标记语言CSS插入样式表的方法有三种:对象&#xff0c;类&#xff0c;实例一个完整的 JavaScript 实现是由以下 3 个不同部分组成的&#xff1a;JavaScript 用法什么是 Java Server Pages?JSP 注释JSP 的 J…

《Linux运维实战:Centos7.6使用haproxy部署rabbitmq3.9.16高可用镜像模式集群》

一、集群架构 说明&#xff1a;对于消息的生产和消费者可以通过HAProxy的软负载将请求分发至RabbitMQ集群中的Node1&#xff5e;Node7节点&#xff0c;其中Node8&#xff5e;Node10的三个节点作为磁盘节点保存集群元数据和配置信息。 二、环境信息 环境信息如下&#xff1a; …

剑指 Offer 52. 两个链表的第一个公共节点

摘要 剑指 Offer 52. 两个链表的第一个公共节点 一、两个链表的第一个公共节点解析 使用双指针的方法&#xff0c;可以将空间复杂度降至 O(1)。只有当链表 headA headB都不为空时&#xff0c;两个链表才可能相交。因此首先判断链表 headA和 headB是否为空&#xff0c;如果其…

[watevrCTF 2019]Timeout 题解

事不一而理有定在&#xff0c;犹百川万折&#xff0c;必归于海。 ——《容斋随笔》 1.查壳 是一个64位像是ELF的文件 无壳子 2.拖入64位IDA IDA分析失败 是一个.com文件 COM格式文件是一种简单的可执行文件。在迪吉多公司于20世纪70年代推出的操作系统中&#xff0c;.COM被用…

c/c++开发,无可避免的函数指针使用案例

一、函数指针简介 函数指针是指指向函数而非指向对象的指针。像其他指针一样&#xff0c;函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定&#xff0c;而与函数名无关。例如&#xff1a; char* (*pf1)(char * p1,char *p2); 这是一个函数指针&#xff0c;其…

javassm超市在线配送管理系统

为了解决用户便捷地在网上购物&#xff0c;本文设计和开发了一个超市管理系统。本系统是基于web架构设计&#xff0c;SSM框架 &#xff0c;使用Mysql数据库管理&#xff0c;综合采用JSP模式来完成系统的相关功能。主要实现了管理员与用户的注册与登陆&#xff0c;个人中心、用户…

[标准库]STM32F103R8T6 高级定时器--PWM输出和带死区互补PWM输出

前言 STM32F103系列的MCU&#xff0c;相比普通的51单片机&#xff0c;在输出硬件PWM这个功能上要强不少&#xff0c;两者实现的方式都类似&#xff0c;都是通过一个定时器来启用硬件PWM输出&#xff0c;不过在输出PWM通道的数量上&#xff0c;32F103要强上不少。仅通过一个高级…

5.Redis 实现点赞 优化登陆(验证码 token..)

Redis&#xff08;1&#xff09;简介Redis 是一个高性能的 key-value 数据库原子 – Redis的所有操作都是原子性的。多个操作也支持事务&#xff0c;即原子性&#xff0c;通过MULTI和EXEC指令包起来。非关系形数据库数据全部存在内存中&#xff0c;性能高。&#xff08;2&#…

Docker中安装MySQL 8

前言 上一期在Windows中安装好了Docker环境&#xff0c;这一期在Docker中完成MySQL 8安装和配置。 启动Docker Desktop后在cmd窗口中输入docker -v即可查看到安装的docker版本 Docker启动容器的原理流程 Docker启动一个容器(应用)时&#xff0c;大致原理流程如下图&#x…

安全技术与防火墙工具iptables

目录 安全技术 安全技术 补充防水墙 防火墙的分类 按保护范围划分 按实现方式划分 按网络协议划分 iptables iptables的五表五链 三种报文流向 iptables基本语法 数据包常见的控制类型 iptables的基本选项 显示扩展模块 保存规则 持久保存规则 加载规则 开机自…

上岸!轻轻松松打工!Python数据分析证

俗话说的好&#xff0c;活到老学到&#x1f914;这个大内卷的时代掌握一项技能还是很重要&#x1f648;的&#xff0c;这不趁着下班时间的功夫&#xff0c;偷偷去考了个证 就是一个不论含金量&#xff0c;还是对实习和求职都非常有益的一个双协会认证的高含金量证书~BDA数据分析…

电影订票网站的设计与开发

技术&#xff1a;Java、JSP等摘要&#xff1a;随着科技的发展&#xff0c;时代的进步&#xff0c;互联网已经成为了人们生活中不可缺少的一部分&#xff0c;网上购物已然是一种时代的象征。纵观市场&#xff0c;电影行业的发展尤为迅速&#xff0c;电影种类和数量的增多导致客流…

Portraiture全新4.0最新版人像磨皮插件更新内容

Portraiture是一款智能磨皮插件&#xff0c;为Photoshop和Lightroom添加一键磨皮美化功能&#xff0c;快速对照片中皮肤、头发、眉毛等部位进行美化&#xff0c;无需手动调整&#xff0c;大大提高P图效率。全新4版本&#xff0c;升级AI算法&#xff0c;并独家支持多人及全身模式…

ROS小车研究笔记2/11/2023:使用ssh远程登录小车

1 SSH简介&#xff1a; SSH全称Secure Shell&#xff0c;是一种建立在应用层的安全网络协议。其安全性又非对称加密(RSA)实现 对称加密&#xff1a;使用同一密钥对信息进行加密和解密&#xff0c;但是一旦该密钥被窃取就会威胁通信安全 非对称加密&#xff1a;使用公钥和私钥。…

【Java基础】018 -- 面向对象阶段项目下(拼图小游戏扩展)

文章目录切换游戏图片的业务分析:1&#xff0c;所需要的技术点2&#xff0c;分析业务逻辑项目实现步骤&#xff1a;添加组件绑定事件&#xff1a;代码实现登录界面的业务分析&#xff1a;1&#xff0c;所需要的技术点2&#xff0c;分析业务逻辑项目实现步骤&#xff1a;主界面设…

Day885.NextKeyLock加锁规则 -MySQL实战

NextKeyLock加锁规则 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于NextKeyLock加锁规则的内容。 加锁规则前提说明&#xff1a; MySQL 后面的版本可能会改变加锁策略&#xff0c;所以这个规则只限于截止到现在的最新版本&#xff0c;即 5.x 系列 <5.7.24&#…

搭建流媒体推流/拉流服务(RTMP/RTSP/HLS/HTTP-FLV)

一、什么是流媒体流媒体&#xff08;streaming media&#xff09;是指将一连串的媒体数据压缩后&#xff0c;经过网上分段发送数据&#xff0c;在网上即时传输影音以供观赏的一种技术与过程&#xff0c;此技术使得数据包得以像流水一样发送&#xff1b;如果不使用此技术&#x…

ffmpeg转码转封装小工具开发

如下图所示&#xff0c;是本人开发的一个转码转封装小工具 其中目标文件视频编码格式支持&#xff1a;H264&#xff0c;H265&#xff0c;VP8&#xff0c;VP9。 目标文件封装格式支持&#xff1a;mp4,mkv,avi,mov,flv。 目标文件音频编码格式支持两个&#xff0c;COPY和AAC&am…

安全寒假作业nginx反向代理+负载均衡上传webshell重难点+apache漏洞

1.应用场景 负载均衡作为现今解决web应用承载大流量访问问题的一种方案&#xff0c;在真实环境中得到广泛的部署。实现负载均衡的方式有很多种&#xff0c;比如 DNS 方式、HTTP 重定向方式、IP 负载均衡方式、反向代理方式等等。 比如基于dns的负载均衡&#xff1a; 当然还有…