STL讲解——模拟实现List

STL讲解——List模拟

list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

开始设计list

因为list是链表,所以先设计一个节点,双向节点。

template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			: _Pre(nullptr)
			, _Next(nullptr)
			, _val(val)
		{}

		ListNode<T>* _Pre;
		ListNode<T>* _Next;
		T _val;
	};

在这里插入图片描述

设计list并且对节点进行初始化。
		void CreateHead()
		{
			_phead = new Node;
			_phead->_Pre = _phead;
			_phead->_Next = _phead;
		}

使头结点自己连接自己
在这里插入图片描述
刚好也可以设计判空函数:首尾相连就是空

bool empty()
{
	return _phead->_Next == _phead->_Pre;
}

设计迭代器

接下来是最难的一步了,也是最容易晕的一部分
在这里插入图片描述
普通迭代器很简单和之前链表(忘记了,请复习数据结构)的思路一模一样。

template<class Tr>
	struct __list_iterator
	{
		typedef ListNode<T> node;
		typedef __list_iterator<T> self;
		typedef node* pnode;
		pnode _pnode;
		__list_iterator(node* n=nullptr)
			:_pnode(n)
		{

		}
		__list_iterator(const self& l)
			:_pnode(l._pnode)
		{

		}
		T& operator*()
		{
			return _pnode->_val;
		}

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

		self& operator++()//前置
		{
			_pnode = _pnode->_Next;
			return _pnode;
		}
		self operator++(int)//后置
		{
			self tmp = *this;
			_pnode = _pnode->_Next;
			return tmp;
		}
		bool operator==(const self& it)
		{
			return _pnode == it._pnode;
		}
		bool operator!=(const self& it)
		{
			return _pnode != it._pnode;
		}

		//不需要析构,因为node的归属权是属于list的,迭代器只是使用权
	}; 

但是如果我需要const iterator怎么办?
难道再设计一个类命名为const iterator?
当然不是了,这就体现出stl大佬思维的灵活性了。
List 的迭代器
迭代器有两种实现方式,具体应根据容器底层数据结构实现:

  1. 原生态指针,比如:vector
  2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
  3. 指针可以解引用,迭代器的类中必须重载operator*()
  4. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
  5. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
    至于operator–()/operator–(int)释放需要重载,根据具体的结构来抉择,双向链表可
    以向前 移动,所以需要重载,如果是forward_list就不需要重载–
  6. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
迭代器的代码:
template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef ListNode<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		typedef node* pnode;
		pnode _pnode;
		__list_iterator(node* n=nullptr)
			:_pnode(n)
		{

		}
		__list_iterator(const self& l)
			:_pnode(l._pnode)
		{

		}
		Ref operator*()
		{
			return _pnode->_val;
		}

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

		self& operator++()//前置
		{
			_pnode = _pnode->_Next;
			return _pnode;
		}
		self operator++(int)//后置
		{
			self tmp = *this;
			_pnode = _pnode->_Next;
			return tmp;
		}
		bool operator==(const self& it)
		{
			return _pnode == it._pnode;
		}
		bool operator!=(const self& it)
		{
			return _pnode != it._pnode;
		}

		//不需要析构,因为node的归属权是属于list的,迭代器只是使用权
	}; 

这个简直神迹一般的代码,因为我不管需要引用还是const引用就会调用Ref就可以了,
需要指针或者const指针,直接调用Ptr就可以了,这些东西都是打包好的。只需要改Ref和Ptr就可以实现调的是不是带const的变量。

增、删、查、改

有了迭代器就可以实现find 、insert、erase。

insert

插入是在pos前的位置插入。插入之后pos不会失效哦。
在这里插入图片描述

push_back\push_front

只需要再最后\最前插入这也元素就可以了(可以复用insert

		void push_back(const T& x)
		{
			ListNode<T>* newNode = new ListNode<T>(x);
			ListNode<T>* tail = _phead->_Pre;
			tail->_Next = newNode;
			newNode->_Pre = tail;
			newNode->_Next = _phead;
			_phead->_Pre = newNode;
		}

在这里插入图片描述
在这里插入图片描述

erase

直接删除pos所在位置,pos会失效,所以要更新pos。
在这里插入图片描述

pop_back\pop_front

其实就是erase的复用

		iterator find(const T& x)
		{
			auto it = begin();
			while (it != end())
			{
				if (*it==x) return it;
				++it;
			}
			return it;
		}
		iterator insert(iterator pos, const T& x)//在pos前面插入
		{
			Node* NewNode = new Node(x);
			Node* cur = pos._pnode;
			Node* prev = cur->_Pre;

			prev->_Next = NewNode;
			NewNode->_Pre = prev;
			NewNode->_Next = cur;
			cur->_Pre = NewNode;
			return iterator(NewNode);

		}
		bool empty()
		{
			return _phead->_Next == _phead->_Pre;
		}
		iterator erase(iterator pos)
		{
			assert(!empty());

			Node* cur = pos._pnode;
			Node* prev = cur->_Pre;
			Node* next = cur->_Next;

			prev->_Next = next;
			next->_Pre = prev;
			delete cur;
			return (iterator)next;
		}

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

		
  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tom王要coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值