list模拟实现

list模拟实现

🔥list的结构

首先,在模拟实现list之前我们先了解一些list的结构。

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

因此,我们若要实现list,则首先需要实现一个结点类 。而一个结点需要存储的信息有:要存储的数据、前一个结点的地址、后一个结点的地址

🔥节点类的实现

这里因为很多地方都要用到这个节点类,所以为了方便,定义成struct的类,这样,成员变量就都是共有的啦。

cpp 复制代码
template <class T>
struct ListNode
{
	ListNode<T>* _prev;
	ListNode<T>* _next;
	T _data;

	ListNode(const T& data = T())
		:_prev(nullptr)
		, _next(nullptr)
		, _data(data)
	{}
};

🔥list的实现

有了节点类之后我们应该怎么,定义一个链表呢?

我们知道,链表其实就是用一个指向头节点的指针管理起来的 ,所以,我们可以定义一个list类,它的成员变量是一个指向头节点的指针,因为,list的底层是一个带头双向循环链表,所以这里的指针,应该指向,带有哨兵位的头节点。

🌈构造函数

list是一个带头双向循环链表,在构造一个list对象时,首先要申请一个头结点,并让其前驱指针和后继指针都指向自己。

cpp 复制代码
list()
{
	_head = new node; //申请头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
}

🌈拷贝构造

方式一:

我们先申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。

因为这里的申请头节点的这个操作经常用所以我们可以封装成一个函数。

cpp 复制代码
void empty_list()
{
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;
}
cpp 复制代码
list(const list<T>& lt)
{
	empty_list();

	for (const auto& e : lt)
	{
		push_back(e);
	}
}

方式二:

我们先实现一个使用迭代器区间构造的函数。

cpp 复制代码
template <class Iterator>
list(Iterator first, Iterator last)
{
	empty_list();//不加会出问题

	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

然后,我们先创建一个临时对象让他利用被拷贝对象的迭代器构造出来,然后再交换,被利用完后的临时对象会在栈帧结束后被清除。

我们先实现swap函数,很简单,交换头节点的指针就可以。

cpp 复制代码
void swap(list<T>& tmp)
{
	std::swap(_head, tmp._head);
}
cpp 复制代码
list(const list<T>& lt)
{
	empty_list();

	list<T> tmp(lt.begin(), lt.end());

	swap(tmp);
}

🔥赋值重载

通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。

cpp 复制代码
list<T>& operator=(list<T> tmp)
{
	swap(tmp);

	return *this;
}

🌈析构函数

首先,我们可以调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可

cpp 复制代码
void clear()
{
	iterator it = begin();

	while (it != end())
	{
		//it = erase(it);
		erase(it++);
	}
}
cpp 复制代码
~list()
{
	clear();

	delete _head;
	_head = nullptr;
}

🔥insert 和 erase

🌈insert

insert函数的作用时在指定迭代器的位置之前插入一个数据。

cpp 复制代码
iterator insert(iterator pos, const T& x)
{
	Node* node = pos._node;//记录当前节点
	Node* prev = node->_prev;//记录前驱节点

	Node* newnode = new Node(x);//记录要插入节点

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

	return iterator(newnode);
}

🌈erase

erase函数用来删除指定迭代器位置的数据。

cpp 复制代码
iterator erase(iterator pos)
{
	assert(pos != end());//防止删除头节点
	
	Node* del = pos._node;//记录要删除节点
	Node* prev = del->_prev;//记录前驱节点
	Node* next = del->_next;//记录后续节点

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

	return iterator(next);//返回所给迭代器pos的下一个迭代器,防止迭代器失效
}

🔥尾插,尾删,头插,头删

这里直接复用insert和erase即可。

cpp 复制代码
// List的插入和删除
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()); 
}

🔥访问容器相关函数

cpp 复制代码
T& front()
{
	return _head->_next->_data;
}

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

T& back()
{
	return _head->_prev->_data;
}

const T& back() const
{
	return _head->_prev->_data;
}

🔥其他函数

size

size函数用于获取当前容器当中的有效数据个数,因为list是链表,所以需要通过遍历的方式统计有效数据的个数。

cpp 复制代码
size_t size() const
{
	size_t count = 0;

	Node* pcur = _head->_next;
	while (pcur != _head)
	{
		count++;
		pcur = pcur->_next;
	}

	return count;
}

resize

resize函数的规则:

1,若当前容器的oldsize小于所给newsize,则尾插结点,直到oldsize等于newsize为止。

2,若当前容器的oldsize大于所给newsize,则只保留前newsize个有效数据。

cpp 复制代码
void resize(size_t newsize, const T& data = T())
{
	size_t oldsize = size();

	if (newsize <= oldsize)
	{
		while (newsize != oldsize)
		{
			pop_back();
			oldsize--;
		}
	}
	else
	{
		while (oldsize != newsize)
		{
			push_back(data);
			oldsize++;
		}
	}
}

🔥迭代器实现

对于,vector迭代器来说我们可以使用原生指针来实现,因为vector的空间时连续的,_start时指向第一个数据的指针,_start++就指向下一个位置了

但是对于list来说,它的数据不是连续存储的而是通过一个一个节点通过指针连接到一起的 ,所以,_head++,并不能到下一个数据的位置,但是可以通过_head = _head->_next实现,所以这里我们可以使用节点的指针单独封装一个类,通过运算符重载模拟指针的行为。

cpp 复制代码
template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	Node* _node;

	typedef ListIterator<T,Ref,Ptr> self;

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

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

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

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

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

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

🌈迭代器类的模板参数说明

cpp 复制代码
template<class T, class Ref, class Ptr>

在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器

cpp 复制代码
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

所以,迭代器类的模板参数列表当中的RefPtr分别代表的是引用类型和指针类型。

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

🌈begin和end

begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。

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

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

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

const_iterator end() const
{
	return const_iterator(_head);
}

全部代码实现

cpp 复制代码
namespace hb
{
	template <class T>
	struct ListNode
	{
		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _data;

		ListNode(const T& data = T())
			:_prev(nullptr)
			, _next(nullptr)
			, _data(data)
		{}
	};

	template <class T,class Ref,class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		Node* _node;

		typedef ListIterator<T,Ref,Ptr> self;

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

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

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

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

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

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

	template <class T>
	struct ListConstIterator
	{
		typedef ListNode<T> Node;
		Node* _node;

		typedef ListConstIterator<T> self;

		ListConstIterator(Node* node)
			:_node(node)
		{}

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

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

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

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

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


	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 ListConstIterator<T> const_iterator;

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

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

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		void empty_list()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}

		list()
		{
			empty_list();
		}

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();

			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}



		/*list(const list<T>& lt)
		{
			empty_list();

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}*/

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

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_list();//不加会出问题

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		list(const list<T>& lt)
		{
			empty_list();

			list<T> tmp(lt.begin(), lt.end());

			swap(tmp);
		}

		list<T>& operator=(list<T> tmp)
		{
			swap(tmp);

			return *this;
		}

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

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

			tail->_next = newnode;
			_head->_prev = newnode;*/

			insert(end(),x);
		}

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

		void pop_back()
		{

			erase(--end());
		}

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

		iterator insert(iterator pos, const T& x)
		{
			Node* node = pos._node;
			Node* prev = node->_prev;

			Node* newnode = new Node(x);

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

			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			

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

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

			return iterator(next);
		}

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

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

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

		T& back()
		{
			return _head->_prev->_data;
		}

		const T& back() const
		{
			return _head->_prev->_data;
		}

		size_t size() const
		{
			size_t count = 0;

			Node* pcur = _head->_next;
			while (pcur != _head)
			{
				count++;
				pcur = pcur->_next;
			}

			return count;
		}

		void resize(size_t newsize, const T& data = T())
		{
			size_t oldsize = size();

			if (newsize <= oldsize)
			{
				while (newsize != oldsize)
				{
					pop_back();
					oldsize--;
				}
			}
			else
			{
				while (oldsize != newsize)
				{
					push_back(data);
					oldsize++;
				}
			}
		}
		
	private:
		Node* _head;
};
相关推荐
霁月风28 分钟前
设计模式——适配器模式
c++·适配器模式
ChoSeitaku1 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程1 小时前
双向链表专题
数据结构
香菜大丸1 小时前
链表的归并排序
数据结构·算法·链表
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i1 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1071 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客1 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
AaVictory.2 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
爱吃喵的鲤鱼2 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++