list容器模拟实现

news2024/12/28 19:29:24

📋 个人简介

  • 💖 作者简介:大家好,我是菀枯😜

  • 🎉 支持我:点赞👍+收藏⭐️+留言📝

  • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

    v2-af3cfbda99d44f8eb114cc6c74c92253_720w

前言

好久不见啊!今天我们的任务是自己从头到尾实现一个STL中的list容器。

list的实现

list的介绍

之前我们实现了STL中的vector容器,vector是一个变长数组。而list在使用上和vector相似,都是把我们的数据按顺序存储下来。但二者底层的数据结构有所区别,list的底层数据结构是双向循环带头链表。关于list的接口使用大家去看看文档就好std::list - cppreference.com

list节点的实现

首先我们要清楚list和list的节点这是两个东西,我们需要分别进行设计。

template<typename T>
struct listNode
{
	listNode<T>* _pre; //指向前一个节点的指针
	listNode<T>* _next; //指向后一个节点的指针
	T _data; //存放的数据

	listNode(const T& val = T())
		:_pre(nullptr)
		, _next(nullptr)
		, _data(val)
	{}
};

list迭代器的实现

我们在实现vector的迭代器时使用了原生指针,而因为list的节点并不能保证是连续存在的,所以我们没办法再使用原生指针作为list的迭代器。那么我们该怎么去设计它呢?

首先我们需要找到对应节点,所以在迭代器中需要一个指针来指向这个节点。然后这个迭代器要能对这个节点进行正确的操作,所以我们要为这个迭代器编写出对应的方法。最后因为STL中的list为一个双向带头循环链表,所以我们的迭代器也应该要支持前后的移动。

接下来我们来看看代码怎么写:

template<typename T, typename Ref, typename Ptr>
struct _list_iterator
{
	typedef listNode<T> Node;
	typedef _list_iterator<T, Ref, Ptr> self;
	Node* _node; //指向对应节点的指针

	_list_iterator(Node* node = nullptr)
		:_node(node)
	{}
	//节点的获取
	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->_pre;
		return *this;
	}

	self operator--(int)
	{
		self tmp(*this);
		_node = _node->_pre;
		return tmp;
	}
	//迭代器的关系运算
	bool operator!= (const self& it)
	{
		return _node != it._node;
	}

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

list的结构

我们已经做好了所有的准备工作,接下来我们该想想如何去维护这个双向带头循环链表了。

其实我们只需要一个指针就可以去维护这个链表,我们只需要一个指针去指向头,我们就可以找到这个链表中的其他所有节点了。

template<typename T>
class list
{
public:
	typedef listNode<T> Node;
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;
private:
	Node* _head;
    ...

list的迭代器接口

首先我们要实现的应该就是begin()和end(),找到链表的首尾之后才方便我们进行其他的操作。想想我们现在有什么,好像只有一个指向头节点的指针,如何用这个指针来找链表的头尾呢?再来看看这个结构。

image-20221209174604803

我们发现头节点的下一个节点就是begin,而头节点的上一个指针就是end,那么这两个接口就很简单了。

iterator begin()
{
	return iterator(_head->_next);
}

iterator end()
{
	return iterator(_head);
}

image-20221209180430198

在这里有的朋友可能会疑惑为什么end()是用头指针来进行构造?因为在STL中的接口都采取了左闭右开的设计方式。如果我们使用head->next来构造end接口的话,会导致我们无法访问最后一个接口。

1670579449089

然后我们来实现剩余的接口

size_t size()
{
    size_t num = 0;
    iterator it = begin();
    //从头结点开始往后一个一个数
    while (it != end())
    {
        ++num;
        ++it;
    }
    return num;
}

//如果为空那么头结点的下一个也为头节点
bool empty()
{
    return _head == _head->_next;
}
//T为元素类型
T &front()
{
    assert(!empty());
    return *begin();
}

T &back()
{
    assert(!empty());
    return *(--end());
}

list的元素操作

插入操作

首先我们来写一下可以在指定位置进行插入的insert接口

iterator insert(iterator pos, const T &value)
{
    Node *cur = pos._node;//指向当前节点
    Node *pre = cur->_pre;//指向前一个节点
    Node *newNode = new Node(value);

    newNode->_pre = pre;
    newNode->_next = cur;
    cur->_pre = newNode;
    pre->_next = newNode;
    return iterator(newNode);
}

动画如下:
动画

有了insert,push_front和push_back直接套用insert就行

void push_back(const T &val)
{
    insert(end(), val);
}
void push_front(const T &val)
{
    insert(begin(), val);
}

删除操作

首先我们来写一下可以在指定位置进行删除的erase接口

iterator erase(iterator pos)
{
    assert(!empty());
    Node *cur = pos._node;
    Node *pre = cur->_pre;
    Node *next = cur->_next;

    delete cur;
    pre->_next = next;
    next->_pre = pre;
    return iterator(next);
}

动画如下:

动画

有了erase,pop_front和pop_back直接套用insert就行

void pop_back(const T &val)
{
    insert(--end());
}
void pop_front(const T &val)
{
    insert(begin());
}

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

list的构造和析构函数

构造函数

我在这里也实现了三个使用较多的接口

void empty_inti()
{
    _head = new Node();
    _head->_next = _head;
    _head->_pre = _head;
}

//默认构造函数 构造一个空链表
list()
{
    empty_inti();
}
//迭代器区间构造
template <typename InputIterator>
list(InputIterator first, InputIterator last)
{
    empty_inti();
    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}

//给值构造
list(int n, const T &value = T())
{
    empty_inti();

    while (n--)
    {
        push_back(T);
    }
}

析构函数

调用clear清空链表后,将new出来的头节点删除即可

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

完整代码

#include <assert.h>

template<typename T>
struct listNode
{
	listNode<T>* _pre;
	listNode<T>* _next;
	T _data;

	listNode(const T& val = T())
		:_pre(nullptr)
		, _next(nullptr)
		, _data(val)
	{}
};

template<typename T, typename Ref, typename Ptr>
struct _list_iterator
{
	typedef listNode<T> Node;
	typedef _list_iterator<T, Ref, Ptr> self;
	Node* _node;

	_list_iterator(Node* node = nullptr)
		:_node(node)
	{}

	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->_pre;
		return *this;
	}

	self operator--(int)
	{
		self tmp(*this);
		_node = _node->_pre;
		return tmp;
	}

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

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

template<typename T>
class list
{
public:
	typedef listNode<T> Node;
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;

private:
	Node* _head;
public:
	list()
	{
		_head = new Node();
		_head->_next = _head;
		_head->_pre = _head;
	}

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

	template<typename InputIterator>
	list(InputIterator first, InputIterator last)
	{
		empty_inti();
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

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

	bool empty()
	{
		return _head == _head->_next;
	}

	void empty_inti()
	{
		_head = new Node();
		_head->_next = _head;
		_head->_pre = _head;
	}

	list(int n, const T& value = T())
	{
		empty_inti();

		while (n--)
		{
			push_back(T);
		}
	}

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

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

	list& operator=(list lt)
	{
		empty_inti();
		swap(lt);
	}

	iterator begin()
	{
		return iterator(_head->_next);
	}

	iterator end()
	{
		return iterator(_head);
	}

	const_iterator begin() const
	{
		return _head->_next;
	}

	const_iterator end() const
	{
		return _head;
	}

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

	T& back()
	{
		assert(!empty());
		return _head->_pre->_data;
	}

	const T& back() const
	{
		assert(!empty());
		return _head->_pre->_data;
	}

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

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

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

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

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

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

	iterator insert(iterator pos, const T& value)
	{
		Node* cur = pos._node;
		Node* pre = cur->_pre;
		Node* newNode = new Node(value);

		newNode->_pre = pre;
		newNode->_next = cur;
		cur->_pre = newNode;
		pre->_next = newNode;
		return iterator(newNode);
	}

	iterator erase(iterator pos)
	{
		assert(!empty());
		Node* cur = pos._node;
		Node* pre = cur->_pre;
		Node* next = cur->_next;

		delete cur;
		pre->_next = next;
		next->_pre = pre;
		return iterator(next);
	}
};

结语

1647941444633

欢迎各位参考与指导!!!

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

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

相关文章

布局福建市场,维也纳酒店欧暇·地中海酒店能否为投资人带来信心与底气?

近日&#xff0c;锦江酒店&#xff08;中国区&#xff09;“一城一海&#xff0c;暇享好时光”——欧暇地中海漳州长泰凯悦广场店开业典礼隆重举办。 与此同时&#xff0c;锦江酒店&#xff08;中国区&#xff09;维也纳酒店&欧暇地中海酒店品牌厦门推介会也圆满落地。在本…

Able2Extract Professional识别引擎经过微调

Able2Extract Professional识别引擎经过微调 改进的表格检测-现在&#xff0c;您可以在自定义PDF到Microsoft Excel转换过程中更准确地确定类似表格结构和内容的位置。 改进了表与列标题的分离-表识别引擎经过改进&#xff0c;可以检测和识别具有单个标题的多列表。 改进的PDF文…

echarts map地图中绘制浙江省市区县乡镇多级联动边界下钻的最新geojson数据文件获取和更新

文章目录ECharts Map地图的显示GeoJSON数据文件获取在ECharts中绘制浙江省的数据ECharts Map地图的显示 ECharts支持地理坐标显示&#xff0c;专门提供了一个geo组件&#xff0c;在setOption中提供option.geo配置即可显示地图。 option.geo配置中有个map属性&#xff0c;取值…

HBase中的Compaction详解

Compaction的作用 由于memstore每次刷写都会生成一个新的HFile&#xff0c;且同一个字段的不同版本&#xff08;timestamp&#xff09;和不同类型&#xff08;Put/Delete&#xff09;有可能会分布在不同的 HFile 中&#xff0c;因此查询时需要遍历所有的 HFile。为了减少 HFile…

PHPMYADMIN 无法编辑 MYSQL 解决方法

本想通过镜像重新把老站点搭建起来拷贝点文章内容,登录后台时发现忘记了密码,想着通过 PHPMyAdmin 修改 Mysql 数据库内容是非常简答的,万万没想到如下图提示错误:#1030 Get error -1 from storage engine,当时就想到可能因 InnoDB 引擎问题导致,查看在 Mysql 的 my.cnf …

C#学习记录——Windows计算器的制作【实例】

参考《C#从入门到项目实践》边学习&#xff0c;边练习实现。 Windows计算器的制作 此次练习的计算器应用软件在Visual Studio 2019编程环境中开发&#xff0c;是一个简单的窗体应用程序&#xff0c;实现简单的计算器功能。 1、系统功能描述 Windows计算器是利用C#程序设计编…

InputStreamReader构造函数的四种方式实现

InputStreamReader类的构造函数 InputStreamReader(InputStream in) //创建InputStreamReader对象&#xff0c;构造方法中传递输入流&#xff0c;使用默认字符集InputStreamReader(InputStream in, String charsetName) //创建InputStreamReader对象&#xff0c;构造方法中传递…

国考省考行测:主体分析法,高频词往往是主体,没有主体也能说语意主旨,故事型材料对比分析法,积极引申大道理

国考省考行测&#xff1a;主体分析法&#xff0c;高频词往往是主体&#xff0c;没有主体也能说语意主旨&#xff0c;故事型材料对比分析法&#xff0c;积极引申大道理 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#…

Redis主从复制,哨兵模式和集群模式

主从复制 什么是主从复制 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a;故障恢复无法自动化&#xff1b;写操作无法负载均…

JSP ssh网上家具店系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh网上家具店系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模 式开发。开发环境为TOMCAT7.0,M…

shell编程(三)--awk

本以为只是个命令&#xff0c;学起来这就是语言么&#xff0c;参看man手册多试吧 格式 awk pattern{action} <file> ​ A pattern can be: ​ BEGIN ​ END ​ expression 示例&#xff1a; ​ awk {print $0} awk.txt ​ print是打印命令&#xff0c;awk.txt是我们为…

JavaScript—实现手风琴画册

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;前端案例分…

EM算法——投硬币样例实现

理论参考 【机器学习】EM——期望最大&#xff08;非常详细&#xff09; 样例介绍 有c个硬币&#xff0c;每次随机选一个投掷n次&#xff0c;重复执行m次并记录结果。 根据投掷结果计算出每个硬币正面的概率。 每次选择的硬币未知。 过程介绍 随机初始化硬币为正的概率 he…

阿里P8终于整理出:Nginx+jvm+MySQL+Docker+Spring实战技术文档

前言 都说程序员工资高、待遇好&#xff0c; 2022 金九银十到了&#xff0c;你的小目标是 30K、40K&#xff0c;还是 16薪的 20K&#xff1f;作为一名 Java 开发工程师&#xff0c;当能力可以满足公司业务需求时&#xff0c;拿到超预期的 Offer 并不算难。然而&#xff0c;提升…

Java搭建实战毕设项目springboot停车位管理系统源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的毕设项目springboot停车位管理系统源码&#xff0c;适合拿来做毕业设计的同学。可以下载来研究学习一下&#xff0c;本期把这套系统分享给大家。 技术架构 技术框架&#xff1a;jQu…

报表控件ActiveReports帮助高校实现办公、财务管理数字化

国内某知名高校教务处 陈主任&#xff1a; “我们教务处有十分多的文件、文档需要归纳整理&#xff0c;包括整个学校的文件通知公告、一些教务资源、职工工作日志记录、职工考勤记录等&#xff0c;同时还涉及一部分财务信息&#xff0c;职工的工资核算、学生续费的收纳等&…

初始python国度

print()函数 我里面有一个你可以直接使用的函数叫print()&#xff0c;可以将你想展示的东东在IDLE或标准的控制台上显示 print()函数可以输出哪些内容? (1)print()函数输出的内容可以是数字 (2)print()函数输出的内容可以是字符串 (3)print()函数输出的内容可以是含有运算符…

模拟电子技术(八)功率放大电路

&#xff08;四&#xff09;放大电路的频率响应电路基础复习功率放大电路的特点功率放大电路的分类互补功率放大电路&#xff08;OCL&#xff09;OCL的计算分析电路基础复习 功率放大电路的特点 以共射放大电路为例 电源功率消耗在了输出回路中&#xff08;Rb上的忽略&#…

智慧天气系统 - 可视化大屏(Echarts)管理系统(HTTP(S)协议)物联网平台(MQTT协议)

一. 智慧天气系统功能定义 天气数据实时监控&#xff0c;实时视频监控&#xff0c;历史数据分析&#xff1b;电子地图&#xff0c;设备地理位置精确定位&#xff1b;多级组织结构管理&#xff0c;满足集团大客户需求&#xff1b;可视化大屏展示&#xff0c;数据指标一目了然&am…

前端小技能:Chrome DevTools中的操作技巧

文章目录 前言I 操作技巧1.1 编辑页面上的任何文本 ✍1.2 清空缓存并硬性重新加载1.3 Command 菜单see aslo前言 Mac 使用 command+option+I 即可打开DevTools I 操作技巧 1.1 编辑页面上的任何文本 ✍ 在控制台输入document.body.contentEditable="true"开启文本…