list的使用以及模拟实现

news2025/4/18 22:42:02

本章目标

1.list的使用
2.list的模拟实现

1.list的使用

在这里插入图片描述
在stl中list是一个链表,并且是一个双向带头循环链表,这种结构的链表是最优结构.
因为它的实现上也是一块线性空间,它的使用上是与string和vector类似的.但相对的因为底层物理结构上它并不像vector是线性连续的,它并没有重载像[]这样的运算符,也没有重载流插入流提取这样的操作.
在这里的使用我们只介绍它的最为特别的接口

1.1sort

在这里插入图片描述

在这里list专门提供了一个sort接口用来排序,因为list的迭代器原因它并不能使用算法库中的sort,并且两个sort的底层并不相同.
在这里插入图片描述
在算法库中要求的sort是一个随机迭代器.而对于list来说,因为它底层的实现原因,它的底层是一个双向迭代器.
在这里插入图片描述
在c++中一共有三种迭代器.
随机迭代器,双向迭代器,单向迭代器
在这里插入图片描述
对于这三种迭代器它们支持的操作是从右到左依此兼容的.
在这里插入图片描述
例如要求双向迭代器的reverse接口是可以传随机迭代器的
在这里插入图片描述
对于这两个sort来说,算法库中的sort的底层是快排,而在list容器中的sort底层是用的是归并排序.
因为双向迭代器是不支持相减操作的,而在快排中有一个找基准值的过程,我们会用左右端点相减来去取基准值防止排序的时间复杂度达到n方的程度.
并且这个list中的sort的效率并不如算法库中的sort效率高

1.2remove和remove_if

而者整体的逻辑都是删除特定值,但是remove是移除给你传过去val相等的值,remove_If是删除满足特定条件的值
在这里插入图片描述

在这里插入图片描述

1.3splice和merge

在这里插入图片描述
在这里插入图片描述
我们先说第一个接口,这个接口的作用是两个list相互拼接起来.
在这里插入图片描述
在这里插入图片描述
我们可以拼接整个链表也可拼接某个迭代器位置的,也可以拼接某个迭代器区间.
但是我们用来拼接的那个链表被拼接的地方将会消失
在这里插入图片描述
我们再来说第二个接口,它的作用是用来合并两个有序list到一个list中,
在这里我们提供一段代码供大家测试

#include <iostream>
#include <list>

// compare only integral part:
bool mycomparison (double first, double second)
{ return ( int(first)<int(second) ); }

int main ()
{
  std::list<double> first, second;

  first.push_back (3.1);
  first.push_back (2.2);
  first.push_back (2.9);

  second.push_back (3.7);
  second.push_back (7.1);
  second.push_back (1.4);

  first.sort();
  second.sort();

  first.merge(second);

  // (second is now empty)

  second.push_back (2.1);

  first.merge(second,mycomparison);

  std::cout << "first contains:";
  for (std::list<double>::iterator it=first.begin(); it!=first.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;

1.4unique

在这里插入图片描述

这个接口的作用是用来去除当前list中相同的元素,但是去除相同的元素是有要求的,它是要求我们的list是有序的.

2.list的模拟实现

因为list的底层并不是一块连续的物理空间,在不同的stl版本中实现并不相同,c++标准并没有规定他是怎么实现的,我们只需要将其进行封装,给予接口供其使用即可.
我们在这里实现list的结构是参考gcc的SGI版本的stl.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在SGI版本的stl中,将链表结点,迭代器,链表封装为三个类.
并且链表结点和迭代器的实现,并没有class而是用的struct,将所有成员都开放为公有.
因为我们在使用list的时候,并不会去通过链表结点和迭代器去进行内部的访问,而且因为c++标准并没有明确规定.各个平台环境下实现也各有不同.如果使用就会出现移植性问题.者也算是一种隐形的封装.

2.0list容器的构成

跟据SGI版本的源码,我们也实现三个类,list,list_node,list_iterator

template <class T>
struct List_Node
{
	List_Node* prev;
	List_Node* next;
	T data;
	List_Node(const T& val = T())
		:prev(nullptr)
		, next(nullptr)
		, data(val)
	{
	}
};
template <class T >
struct list_iterator
{
	typedef List_Node<T> Node;
	typedef list_iterator<T> self;
	Node* node;
	list_iterator( Node* val)
		:node(val)
	{
	}
}:
template <class T>
class list
{
	typedef List_Node<T> Node;
		
public:
	list()
{
	head = new Node;
	head->next = head;
	head->prev = head;
}
private:
	Node* head;
	size_t _size;

};

1.在这里面我们将所有需要用到模板的地方都进行了typedef,者样做第一个是为了封装,我们在这里面使用list只给客户提供接口,并不希望,它们,通过通过结点和迭代器进行访问.第二是为了我们实现的方便.
2.对于迭代器,我们主要实现它解引用.我们要通过这个迭代器去访问它的数据.
所以它的底层也因该是一个结点类型的指针,
3.对于结点的构造函数.我们在给val缺省值的时候,给了一个匿名对象,这样如果我们要给的值是一个自定义类型,它就会调用它的构造,如果是内置类型也是调用一个默认构造.

2.1插入删除

因为list底层的结构是一个双向带头循环的链表.它的逻辑上的结构是和我之前发的博客是一致的.
我们在这里涉及底层逻辑,不过多阐述.只提与之有出入的地方.

void push_back(const T& x)
{
	/*Node* tail = head->prev;
	Node* newnode = new Node(x);
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = head;
	head->prev = newnode;*/
	insert(end(), x);
}

在这里我们可以主动实现(注释中的代码)也可以复用后面要实现的insert函数.

2.1.1insert
void insert(iterator pos, const T& val)
{
	//prev newnode  cur 
	Node* prev = pos.node->prev;
	Node* cur = pos.node;
	Node* newnode = new Node(val);
	prev->next = newnode;
	cur->prev = newnode;
	newnode->prev = prev;
	newnode->next = cur;
	++_size;
}

逻辑上和之前的双向链表是一样的但是与之前不同的是,在这里我们传的是一个迭代器.

2.1.2erase
iterator erase(iterator pos)
{
	//prev pos next
	Node* prev = pos.node->prev;
	Node* next = pos.node->next;
	Node* cur = pos.node;
	prev->next = next;
	next->prev = prev;
	delete cur;
	--_size;
	//return next;
	return iterator(next);
}

对于erase来说,要注意迭代器失效的问题.要返回被删位置的迭代器.
在这里我们也给一个用next结点指针初始化的匿名对象.

void push_front(const T& x)
{
	insert(begin(), x);
}
void pop_front()
{
	erase(begin());
}
void pop_back()
{
	erase(--end());
}

对于它们的尾插头插的复用,我们需要实现list中begin(),end(),迭代器++,–等接口.

iterator begin()
{
	return iterator(head->next);
}
iterator end()
{
	return iterator(head);
}

因为begin和end是一对左闭右开的区间.我们又是一个双向链表.
它存储数据的第一个位置begin是头节点的下一个结点.
它的end是最后存储数据的下一个结点.也就是哨兵位.

Ref 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& it)
{
	return node != it.node;
}
bool operator==(const self& it)
{
	return node == it.node;
}

我的迭代器主要是为了遍历修改.而list是需要迭代器是实现这些运算符重载去完成上面的功能的.对于这些运算符重载是和我们之前学过的日期类的整体逻辑是相似的.在这里不做过多赘述.

2.2const迭代器

对于const迭代器来说,我们至于要要让迭代器指向的对象值不被修改即可.我们至于要修改*的运算符重载.,让它返回的对象是一个const对象即可.但是再重新写一个类的开销太大了.我们可以多给一个模板参数.

template <class T ,class Ref,class Ptr>
struct list_iterator
{
	typedef List_Node<T> Node;
	typedef list_iterator<T,Ref,Ptr> self;

用ref代替*运算符重载的返回值

Ref operator*()
{
	return node->data;
}

我们至于要再list里面传两个不同的模板类的参数即可.
本质上还是写了两个类.只不过有了模板,我们实际上是让编译器干了这个活.

	typedef list_iterator<T, T&,T*> iterator;

	typedef list_iterator<T,const T&,const T*>  const_iterator;
	const_iterator begin() const
	{
		return const_iterator(head->next);
	}
	const_iterator end() const
	{
		return const_iterator(head);
	}

再list这个类中,我们再给两个begin,和end的函数重载.
不过它们的返回值就要给const_iterator这个类的匿名对象.

3.其他构造函数,拷贝,赋值运算符重载,析构

它也支持initializer_list的构造.迭代器区间构造.但是它们都面临一个问题,它们要初始化头结点.我们只需要将默认构造那一部分进行封装.

void empty()
{

	head = new Node;
	head->next = head;
	head->prev = head;
	_size = 0;
}

因为list需要进行深拷贝.我们可以先实现list的swap,然后实现拷贝,赋值运算符重载.都可以用现代写法.

void swap(const list<T>& it)
{
	std::swap(head, it.head);
	std::swap(size, it.size);
}
list(const list<T>& it)
{
	empty();
	for (auto& ch : it)
	{
		push_back(ch);
	}
}
list<T>& operator=(list<T> val)
	{
		swap(val);
		return *this;
	}

对于它的析构函数,我们先要实现它的clear函数.clear,函数是清除头结点以外的结点.析构实在clear的基础上再对头节点进行释放.

void clear()
{
	iterator cur = begin();
	while (cur != end())
	{
		cur = erase(cur);
	}
}
~list()
{
	clear();
	delete head;
	head = nullptr;
	cout << "~list" << endl;
}

4.对于自定义类型的->运算符重载

struct C
{
	int a ;
	int b;
	C(int a = 2,int b = 1)
		:a(a)
		,b(b)
	{
	}
};

假设我们有这么一个类.我们对这个类

	list<C> c;
	c.push_back({ 1,2 });
	c.push_back({ 4,3 });
	c.push_back({ 5,2 });
	auto it = c.begin();
	while (it != c.end())
	{
		//cout << it->a << it->b << " ";
		cout << it.operator->()->a << it.operator->()->b << " ";
		it++;
	}
	cout << endl;

我们要访问struct的成员变量的时候,如果这个类没有实现它的流插入流提取运算符重载.我们需要主动取访问它的内部的变量去间接访问.我们可以提前再迭代器实现->去直接访问它的内部的成员变量.

Ptr operator->()
{
	return &node->data;
}
template <class T ,class Ref,class Ptr>
struct list_iterator
{
	typedef List_Node<T> Node;
	typedef list_iterator<T,Ref,Ptr> self;

再这里也存在const对象的问题.我们仍然需要传一个模板参数.
具体参考代码
https://gitee.com/woodcola/c-learning-code/tree/master/list/list

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

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

相关文章

【今日三题】小乐乐改数字 (模拟) / 十字爆破 (预处理+模拟) / 比那名居的桃子 (滑窗 / 前缀和)

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;每日两三题 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 小乐乐改数字 (模拟)十字爆破 (预处理模拟&#xff09;比那名居的桃子 (滑窗 / 前缀和) 小乐乐改数字 (模拟) 小乐乐改数字…

基于 Qt 的图片处理工具开发(一):拖拽加载与基础图像处理功能实现

一、引言 在桌面应用开发中&#xff0c;图片处理工具的核心挑战在于用户交互的流畅性和异常处理的健壮性。本文以 Qt为框架&#xff0c;深度解析如何实现一个支持拖拽加载、亮度调节、角度旋转的图片处理工具。通过严谨的文件格式校验、分层的架构设计和用户友好的交互逻辑&am…

44、Spring Boot 详细讲义(一)

Spring Boot 详细讲义 目录 Spring Boot 简介Spring Boot 快速入门Spring Boot 核心功能Spring Boot 技术栈与集成Spring Boot 高级主题Spring Boot 项目实战Spring Boot 最佳实践总结 一、Spring Boot 简介 1. Spring Boot 概念和核心特点 1.1、什么是 Spring Boot&#…

虽然理解git命令,但是我选择vscode插件!

文章目录 2025/3/11 补充一个项目一个窗口基本操作注意 tag合并冲突已有远程&#xff0c;新加远程仓库切换分支stash 只要了解 git 的小伙伴&#xff0c;应该都很熟悉这些指令&#xff1a; git init – 初始化git仓库git add – 把文件添加到仓库git commit – 把文件提交到仓库…

idea 打不开terminal

IDEA更新到2024.3后Terminal终端打不开的问题_idea terminal打不开-CSDN博客

【JVM】JVM调优实战

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…

FPGA_DDR(二)

在下板的时候遇到问题 1&#xff1a;在写一包数据后再读&#xff0c;再写再读 这时候读无法读出 查看时axi_arready没有拉高 原因 &#xff1a; 由于读地址后没有拉高rready,导致数据没有读出卡死现象。 解决结果

【吾爱出品】[Windows] 鼠标或键盘可自定义可同时多按键连点工具

[Windows] 鼠标或键盘连点工具 链接&#xff1a;https://pan.xunlei.com/s/VONSFKLNpyVDeYEmOCBY3WZJA1?pwduik5# [Windows] 鼠标或键盘可自定义可同时多按键连点工具 就是个连点工具&#xff0c;功能如图所示&#xff0c;本人系统win11其他系统未做测试&#xff0c;自己玩…

vue3实战一、管理系统之实战立项

目录 管理系统之实战立项对应相关文章链接入口&#xff1a;实战效果登录页&#xff1a;动态菜单&#xff1a;动态按钮权限白天黑夜模式&#xff1a;全屏退出全屏退出登录&#xff1a;菜单收缩&#xff1a; 管理系统之实战立项 vue3实战一、管理系统之实战立项&#xff1a;这个项…

设计模式 Day 6:深入讲透观察者模式(真实场景 + 回调机制 + 高级理解)

观察者模式&#xff08;Observer Pattern&#xff09;是一种设计结构中最实用、最常见的行为模式之一。它的魅力不仅在于简洁的“一对多”事件推送能力&#xff0c;更在于它的解耦能力、模块协作设计、实时响应能力。 本篇作为 Day 6&#xff0c;将带你从理论、底层机制到真实…

汽车软件开发常用的需求管理工具汇总

目录 往期推荐 DOORS&#xff08;IBM &#xff09; 行业应用企业&#xff1a; 应用背景&#xff1a; 主要特点&#xff1a; Polarion ALM&#xff08;Siemens&#xff09; 行业应用企业&#xff1a; 应用背景&#xff1a; 主要特点&#xff1a; Codebeamer ALM&#x…

AI 越狱技术剖析:原理、影响与防范

一、AI 越狱技术概述 AI 越狱是指通过特定技术手段&#xff0c;绕过人工智能模型&#xff08;尤其是大型语言模型&#xff09;的安全防护机制&#xff0c;使其生成通常被禁止的内容。这种行为类似于传统计算机系统中的“越狱”&#xff0c;旨在突破模型的限制&#xff0c;以实…

推荐一款Nginx图形化管理工具: NginxWebUI

Nginx Web UI是一款专为Nginx设计的图形化管理工具&#xff0c;旨在简化Nginx的配置与管理过程&#xff0c;提高开发者和系统管理的工作效率。项目地址&#xff1a;https://github.com/cym1102/nginxWebUI 。 一、Nginx WebUI的主要特点 简化配置&#xff1a;通过图形化的界…

Fay 数字人部署环境需求

D:\ai\Fay>python main.py pygame 2.6.1 (SDL 2.28.4, Python 3.11.9) Hello from the pygame community. https://www.pygame.org/contribute.html [2025-04-11 00:10:16.7][系统] 注册命令... [2025-04-11 00:10:16.8][系统] restart 重启服务 [2025-04-11 00:10:16.8][…

python:all列表

1.all列表的说明&#xff1a; 当模块中有__all__变量时&#xff0c;当使用from xxx import *时&#xff0c;只能导入这个列表中的元素。 2.具体的例子&#xff1a; 1.先创建一个模块my_mod,在列表__all__中分别写入第一次只写入test1&#xff0c;第二次写入test1、test2两个…

基于 SpringBoot 的校园论坛系统

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…

深度学习总结(6)

随机梯度下降 给定一个可微函数&#xff0c;理论上可以用解析法找到它的最小值&#xff1a;函数的最小值就是导数为0的点&#xff0c;因此只需找到所有导数为0的点&#xff0c;然后比较函数在其中哪个点的取值最小。将这一方法应用于神经网络&#xff0c;就是用解析法求出损失…

SpringBoot实战1

SpringBoot实战1 一、开发环境&#xff0c;环境搭建-----创建项目 通过传统的Maven工程进行创建SpringBoot项目 &#xff08;1&#xff09;导入SpringBoot项目开发所需要的依赖 一个父依赖&#xff1a;&#xff08;工件ID为&#xff1a;spring-boot-starter-parent&#xf…

阿里云域名证书自动更新acme.sh

因为阿里云的免费证书只有三个月的有效期&#xff0c;每次更换都比较繁琐&#xff0c;所以找到了 acme.sh&#xff0c;还有一种 certbot 我没有去了解&#xff0c;就直接使用了 acme.sh 来更新证书&#xff0c;acme.sh 的主要特点就是&#xff1a; 支持多种 DNS 服务商自动化续…

大数据Hadoop(MapReduce)

MapReduce概述 MapReduce定义 MapReduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上…