C++ 简单模拟实现 STL 中的 list 与 queue

news2024/10/8 12:12:50

目录

一,list

1, list 的节点与迭代器

2,list 的数据结构、一些简单的功能、构造函数

3,list 的对元素操作

4,C++ 11 的一些功能

5,完整代码:

二,queue


一,list

std::list 是 C++ 标准模板库 (STL) 中提供的一个容器模板,它被实现为环状双向链表。list 和 vector 是两个最常被使用的容器。相较于 vector 的连续线性空间,list 的非连续存储就显得复杂许多,它的好处是每次插人或删除一个元素,就配置或释放一个元素空间。因此,list 对空间的使用一点也不浪费。而且,对于任何位置的插入或移除元素,list 永远是常数时间。

关于 list 的各种接口的用法这里就不介绍了,此文主要是模拟实现。
 

1, list 的节点与迭代器

list 本身和 list 的节点是不同的结构,是需要分开设计的。list 中会包含 list 节点结构。节点结构:

template<class T>
struct list_node {
	list_node<T>* next, * pre;	// next 指向下一个节点,pre 指向前一个节点
	T data;
	list_node(const T& data_ = T())
		:next(nullptr),pre(nullptr),data(data_){}
};

如果不清楚迭代器是什么或者是不清楚要怎么设计一个容器专属的迭代器可以看看这个:C++ 迭代器与反向迭代器-CSDN博客

如果对 vector 的实现细节感兴趣可以看看这个:C++ 简单模拟实现 STL 中的 vector 与 stack-CSDN博客

list 不再能够像 vector 一样以普通指针作为迭代器,因为其节点不保证在储存空间中连续存在。所以 list 迭代器必须有能力指向 list 的节点,并且能够进行正确的递增(++)、递减(--)、取值(*)、成员存取(->)等操作。这里要注意,取值时取的是节点的数据值,不是节点本身,成员取用时取用的是节点的成员。例如:

mySTL::list<int> lt = { 1,2,3,4,5 };
for (auto it = lt.begin();it != lt.end();++it) {
	cout << *it << " ";    // 输出的是 1 2 3 4 5;
}
mySTL::list<pair<int, int>> lt2 = { {1,2},{3,4},{5,6} };
for (auto it = lt2.begin();it != lt2.end();++it) {
	cout << it->first << "," << it->second << " # ";	// 输出的是 1,2 # 3,4 # 5,6 #
}

list 迭代器的设计:

template<class T,class Ref,class Ptr>
struct list_iterator {

	typedef list_node<T> Node;
	typedef list_iterator<T, Ref, Ptr> self;
	typedef list_iterator<T, T&, T*> iterator;

	// 迭代器内部的一个普通指针类型,指向对应的 list 节点
	Node* _node;

	// 迭代器相应类型的定义
	typedef T	value_type;
	typedef Ref reference;
	typedef Ptr pointer;

	// 迭代器的构造
	list_iterator(Node* node):_node(node){}					// 用节点构造当前迭代器
	list_iterator(const iterator& iter):_node(iter._node){}	// 用普通迭代器构造当前迭代器

	// 注意, 对迭代器解引用, 取出来的是节点里面的数据值
	reference operator*()const { return _node->data; }
	// &(operator*()), 这是一个标准的做法
	pointer operator->()const { return &(operator*()); }

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

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

};

2,list 的数据结构、一些简单的功能、构造函数

list 是一个带有头节点的环状双向链表,它只需要一个指针就可以完整的表示整个链表,为了方便操作,我们也可以来额外来维护一个 size 变量来表示节点的个数(不包括头节点)。

template<class T>
class list {
	// ...
private:
	typedef list_node<T> Node;
	typedef list<T> self;

private:
	Node* _head = nullptr;  //头节点
	size_t _size = 0;

	void initialize() {		//初始化头节点
		_head = new Node;
		_head->pre = _head->next = _head;
	}
	// ...
};

list 中有关迭代器的操作与一些简单的基础功能:

template<class T>
class list {
	// ...
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; }

	/*反向迭代器*/
	typedef Reverse_Iterator<iterator> reverse_iterator;
	typedef Reverse_Iterator<const_iterator> const_reverse_iterator;

	reverse_iterator rbegin() { return end(); }
	reverse_iterator rend() { return begin(); }
	const_reverse_iterator rbegin()const { return end(); }
	const_reverse_iterator rend()const { return begin(); }

public:
	// 简单的基础功能
	size_t size() { return _size; }
	bool empty() { return _size == 0; }
	void clear() {
		while (not empty()) pop_back();
	}

	void swap(list& lt) {
		std::swap(_head, lt._head);
		std::swap(_size, lt._size);
	}

	// 对首尾元素的访问
	T& back() { return *(--end()); }
	const T& back()const { return *(--end()); }
	T& front() { return *begin(); }
	const T& front()const { return *begin(); }
	// ...
};

list 的构造函数:

template<class T>
class list {
	// ...
public:
	//构造
	list() { initialize(); }

	list(size_t n, const T& data = T()) {
		initialize();
		while (n--) { push_back(data); }
	}
	list(int n, const T& data = T()) {
		initialize();
		while (n--) { push_back(data); }
	}

    /*使用迭代器构造*/
	template<class input_iterator>						
	list(input_iterator begin, input_iterator end) {
		initialize();
		while (begin != end) {
			push_back(*(begin++));
		}
	}

	//拷贝
	list(const list& lt) {
		initialize();
		for (const auto& data : lt) {
			push_back(data);
		}
	}

	self& operator=(list lt) {
		swap(lt);
		return *this;
	}

	//析构
	~list() {
		clear();
		delete _head;
		_head = nullptr;
		_size = 0;
	}
	// ...
};

3,list 的对元素操作

std::list 里面提供的元素操作有很多,这里就只挑几种重要的函数来实现。我们把 insert 与 erase 操作实现之后就可以很轻松的实现 push_back()、push_front()(尾插,头插) 与 pop_back() 、pop_front() (尾删,头删)操作了。

template<class T>
class list {
	// ...
public:
	//增
	void push_front(const T& data) { insert(begin(), data); }	// 头插新节点
	void push_back(const T& data) { insert(end(), data); }		// 尾插新节点
	// 在 pos 位置的前面插入一个值为 data 的新节点, 返回新插入的节点的迭代器
	iterator insert(iterator pos, const T& data) {
		Node* node = pos._node;
		Node* newNode = new Node(data);

		newNode->next = node;
		newNode->pre = node->pre;

		node->pre->next = newNode;
		node->pre = newNode;
		++_size;
		return newNode;
	}

	//删
	void pop_front() { erase(begin()); }	// 头删
	void pop_back() { erase(--end()); }		// 尾删
	// 删掉迭代器 pos 所指向的节点, 返回删掉的节点的位置的新节点迭代器
	iterator erase(iterator pos) {
		assert(pos != end());
		Node* tmp = pos._node;
		tmp->pre->next = tmp->next;
		tmp->next->pre = tmp->pre;
		Node* res = tmp->next;
		delete tmp;
		--_size;
		return res;
	}
	// ...
};

4,C++ 11 的一些功能

这里主要实现的功能是 initializer_list 初始化右值引用,如果对这两个东西不了解的话可以看看这两篇博客:

C++11 一些常用的功能-CSDN博客

C++ 左值引用与右值引用-CSDN博客

template<class T>
class list {
	// ...
public:
	//C++ 11 
	//initializer_list构造
	list(const std::initializer_list<T>& lt) {
		initialize();
		for (const T& data : lt) {
			push_back(data);
		}
	}

	//右值引用相关
	//移动构造与移动赋值
	list(list&& lt) :_head(lt._head), _size(lt._size) {
		initialize();
		lt._head = nullptr;
		lt._size = 0;
	}

	self& operator=(list&& lt) {
		delete* this;
		std::swap(_head, lt._head);
		std::swap(_size, lt._size);
	}

	//插入
	void push_front(T&& data) { insert(begin(), std::forward<T>(data)); }
	void push_back(T&& data) { insert(end(), std::forward<T>(data)); }
	iterator insert(iterator pos, T&& data) {
		Node* node = pos._node;
		Node* newNode = new Node(std::forward<T>(data));

		newNode->next = node;
		newNode->pre = node->pre;

		node->pre->next = newNode;
		node->pre = newNode;
		++_size;
		return newNode;
	}
	// ...
};

5,完整代码:

namespace mySTL {

	// list 节点类
	template<class T>
	struct list_node {
		list_node<T>* next, * pre;	// next 指向下一个节点,pre指向前一个节点
		T data;
		list_node(const T& data_ = T())
			:next(nullptr),pre(nullptr),data(data_){}
	};

	// list 迭代器类
	template<class T,class Ref,class Ptr>
	struct list_iterator {

		typedef list_node<T> Node;
		typedef list_iterator<T, Ref, Ptr> self;
		typedef list_iterator<T, T&, T*> iterator;

		// 迭代器内部的一个普通指针类型,指向对应的 list 节点
		Node* _node;

		// 迭代器相应类型的定义
		typedef T	value_type;
		typedef Ref reference;
		typedef Ptr pointer;

		// 迭代器的构造
		list_iterator(Node* node):_node(node){}					// 用节点构造当前迭代器
		list_iterator(const iterator& iter):_node(iter._node){}	// 用普通迭代器构造当前迭代器

		// 注意, 对迭代器解引用, 取出来的是节点里面的数据值
		reference operator*()const { return _node->data; }
		// &(operator*()), 这是一个标准的做法
		pointer operator->()const { return &(operator*()); }

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

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

	};
	
	// list 类
	template<class T>
	class list {
	private:
		typedef list_node<T> Node;
		typedef list<T> self;

	private:
		Node* _head = nullptr;  //头节点
		size_t _size = 0;

		void initialize() {		//初始化头节点
			_head = new Node;
			_head->pre = _head->next = _head;
		}

	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; }

		/*反向迭代器*/
		typedef Reverse_Iterator<iterator> reverse_iterator;		
		typedef Reverse_Iterator<const_iterator> const_reverse_iterator;

		reverse_iterator rbegin() { return end(); }
		reverse_iterator rend() { return begin(); }
		const_reverse_iterator rbegin()const { return end(); }
		const_reverse_iterator rend()const { return begin(); }

	public:
		// 简单的基础功能
		size_t size() { return _size; }
		bool empty() { return _size == 0; }
		void clear() { 
			while (not empty()) pop_back();
		}

		void swap(list& lt) {
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		// 对首尾元素的访问
		T& back() { return *(--end()); }
		const T& back()const { return *(--end()); }
		T& front() { return *begin(); }
		const T& front()const { return *begin(); }

		// 在迭代器 start 与 finish 构成的范围内查找值为 target 的节点
		iterator find(const iterator& start, const iterator& finish, const T& target) {
			for (iterator it = start;it != finish;++it) {
				if (*it == target) return it;
			}
			return finish;
		}

	public:
		//构造
		list() { initialize(); }

		list(size_t n, const T& data = T()) {
			initialize();
			while (n--) { push_back(data); }
		}
		list(int n, const T& data = T()) {
			initialize();
			while (n--) { push_back(data); }
		}

		template<class input_iterator>						/*使用迭代器构造*/
		list(input_iterator begin, input_iterator end) {
			initialize();
			while (begin != end) {
				push_back(*(begin++));
			}
		}

		//拷贝
		list(const list& lt) {
			initialize();
			for (const auto& data : lt) {
				push_back(data);
			}
		}

		self& operator=(list lt) {
			swap(lt);
			return *this;
		}

		//析构
		~list() {
			clear();
			delete _head;
			_head = nullptr;
			_size = 0;
		}

	public:
		//增
		void push_front(const T& data) { insert(begin(), data); }	// 头插新节点
		void push_back(const T& data) { insert(end(), data); }		// 尾插新节点
		// 在 pos 位置的前面插入一个值为 data 的新节点, 返回新插入的节点的迭代器
		iterator insert(iterator pos,const T& data) {
			Node* node = pos._node;
			Node* newNode = new Node(data);

			newNode->next = node;
			newNode->pre = node->pre;

			node->pre->next = newNode;
			node->pre = newNode;
			++_size;
			return newNode;
		}

		//删
		void pop_front() { erase(begin()); }	// 头删
		void pop_back() { erase(--end()); }		// 尾删
		// 删掉迭代器 pos 所指向的节点, 返回删掉的节点的位置的新节点迭代器
		iterator erase(iterator pos) {
			assert(pos != end());
			Node* tmp = pos._node;
			tmp->pre->next = tmp->next;
			tmp->next->pre = tmp->pre;

			Node* res = tmp->next;
			delete tmp;
			--_size;
			return res;
		}

	public:
		//C++ 11 
		//initializer_list构造
		list(const std::initializer_list<T>& lt) {
			initialize();
			for (const T& data : lt) {
				push_back(data);
			}
		}

		//右值引用相关
		//移动构造与移动赋值
		list(list&& lt) :_head(lt._head), _size(lt._size) {
			initialize();
			lt._head = nullptr;
			lt._size = 0;
		}

		self& operator=(list&& lt) {
			delete *this;
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		//插入
		void push_front(T&& data) { insert(begin(), std::forward<T>(data)); }
		void push_back(T&& data) { insert(end(), std::forward<T>(data)); }
		iterator insert(iterator pos, T&& data) {
			Node* node = pos._node;
			Node* newNode = new Node(std::forward<T>(data));

			newNode->next = node;
			newNode->pre = node->pre;

			node->pre->next = newNode;
			node->pre = newNode;
			++_size;
			return newNode;
		}

	};

}

二,queue

queue 是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。queue 允许新增元素、移除元素、从最底端加人元素取得最顶端元素。但除了最底端可以加入、最顶端可以取出外,没有任何其它方法可以存取 queue 的其它元素。也就是说,queue 不允许有遍历行为。将元素推人queue 的操作称为 push,将元素推出 queue 的操作称为 pop。

queue 与 stack 一样,都是容器适配器(container adapters),他们的底层都是其他的容器,STL 中的 stack 与 queue 实际上都是对其他容器的封装,其都不支持迭代器。

我们可以选择用 list 也可以选择用 deque 来当作 queue 的底层容器。

namespace mySTL {

	template<class T, class Container = list<T>>
	class queue {
	private:
		Container _cont;	// 底层使用的容器
	public:
		void push(const T& data) { _cont.push_back(data); }
		void push(T&& data) { _cont.push_back(std::forward<T>(data)); }
		void pop() { _cont.pop_front(); }

		T& back() { return _cont.back(); }
		const T& back()const { return _cont.back(); }
		T& front() { return _cont.front(); }
		const T& front()const { return _cont.front(); }

		size_t size() { return _cont.size(); }
		bool empty() { return _cont.empty(); }

	};

}

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

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

相关文章

Visual Studio 小更新:改善变量的可见性

在 Visual Studio 2022 17.10 预览版 2 中&#xff0c;我们改善了一些小功能&#xff0c;例如&#xff1a;在调试版本中&#xff0c;变量窗口现已可以显示调用堆栈中任意帧的局部变量。 如需体验此功能&#xff0c;请直接安装最新预览版本&#xff0c;就可以知道是怎么一回事儿…

CSS及javascript

一、CSS简介 css是一门语言&#xff0c;用于控制网页的表现。 cascading style sheet:层叠样式表 二、css的导入方式 css代码与html代码的结合方式 &#xff08;1&#xff09;css导入html有三种方式&#xff1a; 1.内联样式&#xff1a;<div style"color:red&quo…

mac 系统如何生成秘钥

1.打开终端&#xff0c;输入 cd ~/.ssh 进入.ssh目录&#xff0c;输入 ls 检查是否已经存在SSH密钥。如果看到类似 id_rsa.pub 的文件&#xff0c;说明已经有一对公钥和私钥&#xff0c;不用新建&#xff0c;直接查看就可以&#xff0c;如果没有需要生成新的密钥。 2.在终端输…

Unity 视频组件 VideoPlayer

组件添加&#xff1a; 在自己定义的组件下&#xff08;例如&#xff1a;Panel&#xff09; 点击 Inspector 面板中的 AddComponent &#xff0c;输入“VideoPlayer”。 资源 这里 视频资源有两种形式&#xff0c;第一种是 VideoClip &#xff0c;需要将视频文件拖拽到该属性字段…

【C语言】【Leetcode】88. 合并两个有序数组

文章目录 一、题目二、思路再思考 一、题目 链接: link 二、思路 这题属于简单题&#xff0c;比较粗暴的做法就是直接比较两个数组&#xff0c;先把第二个数组加到第一个的后面&#xff0c;如何冒泡排序&#xff0c;这种方法简单粗暴但有效&#xff0c;可是不适用于这题&…

政安晨:【Keras机器学习实践要点】(二)—— 给首次接触Keras 3 的朋友

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras实战演绎机器学习 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 介绍 Keras 3是一个深度学习框架&#xff0…

牛客网BC-33 统计成绩(数组排序思想)

题目如下 --------------------------------------------------------------------------------------------------------------------------------- 思路&#xff1a;以数组形式输入&#xff0c;并将数组顺序&#xff08;或者逆序&#xff09;排序&#xff0c;最后输出最大值最…

Mysql数据库:日志管理、备份与恢复

目录 前言 一、MySQL日志管理 1、存放日志和数据文件的目录 2、日志的分类 2.1 错误日志 2.2 通用查询日志 2.3 二进制日志 2.4 慢查询日志 2.5 中继日志 3、日志综合配置 4、查询日志是否开启 二、数据备份概述 1、数据备份的重要性 2、备份类型 2.1 从物理与…

2.6 IDE(集成开发环境)是什么

IDE&#xff08;集成开发环境&#xff09;是什么 IDE 是 Integrated Development Environment 的缩写&#xff0c;中文称为集成开发环境&#xff0c;用来表示辅助程序员开发的应用软件&#xff0c;是它们的一个总称。 通过前面章节的学习我们知道&#xff0c;运行 C 语言&…

蓝桥杯单片机快速开发笔记——利用定时器计数器设置定时器

一、基本原理 参考本栏http://t.csdnimg.cn/iPHN0 二、具体步骤 三、主要事项 如果使用中断功能记得打开总中断EA 四、示例代码 void Timer0_Isr(void) interrupt 1 { }void Timer0_Init(void) //10毫秒12.000MHz {AUXR & 0x7F; //定时器时钟12T模式TMOD & 0xF0;…

微服务day06 -- Elasticsearch的数据搜索功能。分别使用DSL和RestClient实现搜索

1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff08;Domain Specific Language&#xff09;来定义查询。常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数据&#xff0c;一…

iMazing2024功能强大的iPhone和iPad管理工具

iMazing是一款功能强大的iPhone和iPad管理工具&#xff0c;确实可以作为iTunes的替代品进行数据备份。以下是一些关于iMazing的主要特点和功能&#xff1a; 设备备份&#xff1a;iMazing可以备份iOS设备上的所有数据&#xff0c;包括照片、视频、音乐、应用程序等。与iTunes相比…

docker部署gitlab 报错的问题!!!

1、什么是gitlab&#xff1f; Gitlab是一个用于仓库管理系统的开源项目&#xff0c;使用git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务。Gitlab有乌克兰程序员DmitriyZaporozhets和ValerySizov开发&#xff0c;它由Ruby写成。后来&#xff0c;一些部分用Go语…

leetcode 225.用队列实现栈 JAVA

题目 思路 1.一种是用双端队列&#xff08;Deque&#xff09;&#xff0c;直接就可以调用很多现成的方法&#xff0c;非常方便。 2.另一种是用普通的队列&#xff08;Queue&#xff09;,要实现栈的先入后出&#xff0c;可以将最后一个元素的前面所有元素出队&#xff0c;然后…

tcp和udp分别是什么?udp和tcp的区别

TCP和UDP是计算机网络中常见的两种传输层协议&#xff0c;它们在实际应用中具有不同的特点和用途。本文将对TCP和UDP进行介绍&#xff0c;并分析它们之间的区别。 TCP和UDP分别是什么&#xff1f; TCP&#xff08;Transmission Control Protocol&#xff09; TCP是一种面向连…

基于TensorFlow的花卉识别(算能杯)%%%

Anaconda Prompt 激活 TensorFlow CPU版本 conda activate tensorflow_cpu //配合PyCharm环境 直接使用TensorFlow1.数据分析 此次设计的主题为花卉识别&#xff0c;数据为TensorFlow的官方数据集flower_photos&#xff0c;包括5种花卉&#xff08;雏菊、蒲公英、玫瑰、向日葵…

论文汇总:A Closer Look at Few-shot Classification Again

文章汇总 文章是在总体上再一次地观察如何小样本领域存在的问题&#xff0c;并且发现了较为有趣的规律 1.测试误差随训练类别的数量而下降&#xff0c;而不是随每个类别的训练样本数量而下降。 2.训练算法(me&#xff1a;预训练模型)和自适应算法(me&#xff1a;预训练之后的…

适用于 Mac 电脑的 10 个最佳数据恢复工具

对于任何依对于依赖计算机获取重要文件&#xff08;无论是个人照片还是重要商业文档&#xff09;的人来说&#xff0c;数据丢失可能是一场噩梦。值得庆幸的是&#xff0c;有多种专门为 Mac 用户提供的数据恢复工具&#xff0c;可以帮助检索丢失或意外删除的文件。在本文中&…

SpringBoot 文件上传(三)

之前讲解了如何接收文件以及如何保存到服务端的本地磁盘中&#xff1a; SpringBoot 文件上传&#xff08;一)-CSDN博客 SpringBoot 文件上传&#xff08;二&#xff09;-CSDN博客 这节讲解如何利用阿里云提供的OSS&#xff08;Object Storage Service)对象存储服务保存文件。…

SORT/deepSORT_3.8

目标&#xff1a; SORT算法的原理DeepSORT算法的原理 SORT&#xff08;Simple Online and Realtime Tracking&#xff09;和DeepSORT是两种广泛应用在计算机视觉领域的多目标跟踪算法。 SORT: SORT算法由Martin Danelljan等人于2016年提出&#xff0c;是一种基于关联的在线实…