【C++】list的模拟实现

文章目录

  • [1. list的介绍](#1. list的介绍)
  • [2. 模拟实现](#2. 模拟实现)
    • [2.1 链表的节点](#2.1 链表的节点)
    • [2.2 链表的数据结构](#2.2 链表的数据结构)
    • [2.3 push_back、pop_back](#2.3 push_back、pop_back)
    • [2.4 list的迭代器](#2.4 list的迭代器)
    • [2.5 构造、析构](#2.5 构造、析构)
    • [2.6 insert、erase、push_front、pop_front](#2.6 insert、erase、push_front、pop_front)
    • [2.7 容量、clear、swap](#2.7 容量、clear、swap)
  • [3. 完整代码](#3. 完整代码)

1. list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除 的序列式容器,并且该容器可以前后双向迭代
  2. list的底层是双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  4. 与其他序列式容器相比,list最大的缺陷是不支持任意位置的随机访问;而且list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

2. 模拟实现

2.1 链表的节点

每一个设计过链表的人都知道,链表本身和链表的节点是不同的结构,需要分开设计。

cpp 复制代码
	template <class T>
	struct ListNode
	{
		typedef ListNode<T> Node;
		Node* _next;//后继节点
		Node* _prev;//前驱节点
		T _data;

		//构造函数
		ListNode(const T& data = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

2.2 链表的数据结构

对于list而言,它不仅仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,就可以完整的表现整个链表:

cpp 复制代码
template <class T>
	class list
	{
	public:
		typedef ListNode<T> Node;

		void empty_init()
		{
			_head = new Node;//会调用Node的构造
			//自己成环
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}
	private:
		Node* _head;//哨兵位
	};

2.3 push_back、pop_back

相信学习过数据结构中链表这一部分的伙伴对这两个操作不陌生了吧。

cpp 复制代码
		//尾插
		void push_back(const T& val)
		{
			Node* newnode = new Node(val);//创建新节点

			Node* prev = _head->_prev;

			newnode->_next = _head;
			newnode->_prev = _head->_prev;

			prev->_next = newnode;
			_head->_prev = newnode;
		}
cpp 复制代码
		//尾删
		void pop_back()
		{
			assert(_head->_next != _head);//不能删除哨兵位

			Node* del = _head->_prev;
			Node* prev = del->_prev;

			prev->_next = _head;
			_head->_prev = prev;

			delete del;
			del = nullptr;
		}

2.4 list的迭代器

list的迭代器不能够再像vector一样以普通指针作为迭代器了,因为其节点不保证再存储空间中是连续存在的。list的迭代器也必须有能力指向list的节点,并能够进行正确的递增、递减、取值、成员存取等操作。

所谓list迭代器正确的递增、递减、取值、成员取用操作是指:递增是指向下一个节点,递减是指向上一个节点,取值时取的是节点的数据值,成员取用时取用的时节点的成员。

所以为了达到这一目的,我们可以将list的迭代器放在一个类中,在类中重载迭代器正常的操作。

cpp 复制代码
	template <class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T> Self;

		Node* _node;//指向节点的指针

		ListIterator(Node* node = nullptr)//默认使用空指针构造
			:_node(node)
		{}
	};

重载迭代器的常规操作。

cpp 复制代码
//前置:++it
		Self& operator++()
		{
			//指针后移,指向下一个节点
			_node = _node->_next;

			return *this; //this是ListIterator类型的指针,*this才是ListIterator
		}

		//后置::it++
		Self operator++(int)
		{
			Self tmp(*this);//使用当前迭代器  拷贝构造它
			_node = _node->_next;
			return tmp;
		}

		//前置::--it
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置::it--
		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;
		}

		T& operator*()
		{
			return _node->_data;
		}

		T* operator->()
		{
			return &_node->_data;
		}

要想使用迭代器,需要在list类中实现begin和end方法。

cpp 复制代码
		typedef ListIterator<T> iterator;

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

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

这样,我们就可以使用迭代器遍历我们的链表了。

那const迭代器怎么实现呢?

我们直接在普通迭代器前面加上const可不可行呢?

cpp 复制代码
		iterator begin()
		{
			return iterator(_head->_next);
		}
		
		const iterator begin()const 
		{
			return iterator(_head->_next);
		}

答案是不可行的,因为即使是const迭代器,也应该支持++、- - 等操作。
const迭代器的目标是指向的内容不能改,并不是迭代器本身不能改,类似const T* p

那么const迭代器如何实现上述目的呢?

我们是不是需要再写一个ListConstIterator类,然后该类中的解引用与箭头使用const修饰 ,就可以达到想要的效果。

此时我们的const迭代器就可以跑起来了

但是你觉不觉得现在的代码有点冗余,两个类仅仅只有一点不同,大多数代码都是重复的。有没有什么好的办法解决呢?

我直接在原来模板的基础上加个const行不行呢?

答案是不行的。


所以,我们要如何解决呢?

我们可以使用类的模板参数进行传递,为类设计三个模板参数,各参数之间互不影响。

到此,我们的迭代器就完美了。

2.5 构造、析构

这里就非常简单了,无非就是开空间,拷数据。

cpp 复制代码
		//构造
		void empty_init()
		{
			_head = new Node;//会调用Node的构造
			//自己成环
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

		list(const list<T>& lt)
		{
			empty_init();//哨兵位
			Node* cur = lt._head->_next;
			while (cur != lt._head)
			{
				push_back(cur->_data);
				cur = cur->_next;
			}
		}
		list(size_t n, const T& val = T())
		{
			empty_init();
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}
				//lt2 = lt1
		//传统写法
		//list<T>& operator=(const list<T>& lt)
		//{
		//	empty_init();
		//	for (auto e : lt)
		//	{
		//		push_back(e);
		//	}
		//}
		
		//现代写法
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
		//列表初始化
		list(initializer_list<T> lt)
		{
			empty_init();
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

		~list()
		{
			Node* cur = _head->_next;
			Node* next = cur->_next;
			while (cur != _head)
			{
				delete cur;
				cur = next;
				next = cur->_next;
			}
			delete _head;//释放哨兵位
			_head = nullptr;
		}

2.6 insert、erase、push_front、pop_front

list有一个重要性质,插入操作(insert)和接合操作(splice)都不会造成原有的list迭代器失效 。甚至list的元素删除操作(erase),也只有"指向被删除元素"的那个迭代器失效,其它迭代器均不受影响

cpp 复制代码
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);

			Node* next = pos._node;
			Node* prev = pos._node->_prev;

			newnode->_next = next;
			newnode->_prev = prev;

			next->_prev = newnode;
			prev->_next = newnode;

			return pos;
		}
cpp 复制代码
		iterator erase(iterator pos)
		{
			assert(pos != end());//不能删除哨兵位

			Node* next = pos._node->_next;
			Node* prev = pos._node->_prev;

			prev->_next = next;
			next->_prev = prev;

			delete pos._node;

			return iterator(next);
		}

push_front与pop_front可以复用insert和erase函数。

cpp 复制代码
		void push_front(const T& val)
		{
			insert(begin(), val);
		}

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

2.7 容量、clear、swap

cpp 复制代码
		//容量
		size_t size()
		{
			size_t n = 0;
			Node* cur = _head->_next;
			while (cur != _head)
			{
				n++;
				cur = cur->_next;
			}
			return n;
		}

		bool empty()
		{
			return _head->_next == _head;
		}
cpp 复制代码
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);  //erase会返回删除元素位置后面元素的迭代器
			}
		}

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

3. 完整代码

cpp 复制代码
#include<iostream>
using namespace std;
#include<assert.h>

namespace lt
{
	//节点类
	template <class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
		ListNode(const T& data = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(data)
		{}
	};

	//迭代器类
	template <class T,class Ref,class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		Node* _node;

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

		ListIterator(const Self& it)
		{
			_node = it._node;
		}

		//++it
		Self& operator++()
		{
			this->_node = this->_node->_next;
			return *this;
		}
		//it++
		Self operator++(int)
		{
			Self tmp(*this);   //调用上面的拷贝构造
			this->_node = this->_node->_next;
			return tmp;
		}

		//--it
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//it--
		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;
		}

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

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

	//链表类
	template <class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;
		typedef ReverseIterator<T, const T&, const T*> reverse_iterator;
		
		//------------------------------------------------------------------------
		//构造
		void empty_init()
		{
			_head = new Node;//哨兵位
			_head->_next = _head;
			_head->_prev = _head;
		}
		list()
		{
			empty_init();
		}

		list(const list<T>& lt)
		{
			empty_init();//先整一个哨兵位
			Node* cur = lt._head->_next;
			while (cur != lt._head)
			{
				push_back(cur->_data);
				cur = cur->_next;
			}
		}
		list(size_t n, const T& val = T())
		{
			empty_init();
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		~list()
		{
			Node* cur = _head->_next;
			Node* next = cur->_next;
			while (cur != _head)
			{
				delete cur;
				cur = next;
				next = cur->_next;
			}
			delete _head;
			_head = nullptr;
		}

		list(initializer_list<T> lt)
		{
			empty_init();
			for (const auto e : lt)
			{
				push_back(e);
			}
		}

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

		list<T>& operator=(list<T> lt)
		{
			swap(lt._head);
			return *this; 
		}

		//------------------------------------------------------------------------
		//迭代器
		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(_head->_prev);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(_head);
		}
		//------------------------------------------------------------------------
		//容量
		size_t size()const
		{
			size_t n = 0;
			Node* cur = _head->_next;
			while (cur != _head)
			{
				n++;
				cur = cur->_next;
			}
			return n;
		}

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

		//------------------------------------------------------------------------
		//访问
		T& front()
		{
			//链表不能为空
			assert(_head->_next != _head);
			return _head->_next->_data;
		}
		const T& front()const 
		{
			assert(_head->_next != _head);
			return _head->_next->_data;
		}

		T& back()
		{
			//链表不能为空
			assert(_head->_prev != _head);
			return _head->_prev->_data;
		}
		const T& back()const
		{
			assert(_head->_prev != _head);
			return _head->_prev->_data;
		}

		//------------------------------------------------------------------------
		//修改
		void push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node* prev = _head->_prev;

			newnode->_next = _head;
			newnode->_prev = prev;

			_head->_prev = newnode;
			prev->_next = newnode;
		}

		void pop_back()
		{
			Node* del = _head->_prev;
			Node* prev = del->_prev;

			prev->_next = _head;
			_head->_prev = prev;

			delete del;
			del = nullptr;
		}

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

		void pop_front()
		{
			erase(begin());
		}
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);

			Node* next = pos._node;
			Node* prev = pos._node->_prev;

			newnode->_next = next;
			newnode->_prev = prev;
			prev->_next = newnode;
			next->_prev = newnode;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());//哨兵位不能删除
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;
			return iterator(next);
		}

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

	private:
		Node* _head;
	};
}
相关推荐
tnnnnt9 分钟前
C++多线程学习笔记
c++·多线程
ToBeWhatYouWannaBe.28 分钟前
代码随想录-Day49
java·数据结构·算法·leetcode
Little Tian32 分钟前
插入排序——C语言
c语言·数据结构·算法·排序算法
韩楚风34 分钟前
【手写数据库内核组件】0201 哈希表hashtable的实战演练,多种非加密算法,hash桶的冲突处理,查找插入删除操作的代码实现
c语言·数据结构·数据库·哈希算法·散列表
creative_mind43 分钟前
My Greedy Algorithm(贪心算法)之路(一)
c++·算法·贪心算法
续亮~1 小时前
9、Redis 高级数据结构 HyperLogLog 和事务
数据结构·数据库·redis
王红花x1 小时前
STL——list模拟实现
c++·学习·list
阳光男孩011 小时前
力扣1546.和为目标值且不重叠的非空子数组的最大数目
数据结构·算法·leetcode
donotdothat1 小时前
D1.排序
数据结构·算法
我是陈泽2 小时前
AI教你如何系统的学习Python
开发语言·数据结构·人工智能·python·学习·青少年编程