list模拟实现

list使用介绍

介绍

  1. list是可以在常数范围的任意位置进行插入和删除的序列容器,并且该容器可以前后双向迭代
  2. list底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素
  3. 相较于其他容器,list通常在任意位置进行插入,删除元素的执行效率更好

使用

节点结构
在这里插入图片描述

链表结构
在这里插入图片描述

构造函数

构造函数介绍
list(size_type n,const value_type&val=value_type())构造含有n个val元素的List
list()构造空list
list(const list&x拷贝构造
list(Inputiterator frist,Inputiterator last用(frist,last)区间中的元素构造list

list iterator

函数声明接口说明
begin+end返回第一个元素的迭代器+最后一个元素下一个位置的迭代器
rbegin+rend返回第一个元素的reverse_iterator(end位置),返回最后一个元素下一个位置的reverse_iterator(begin位置)

list capacity

函数声明接口说明
empty返回list第一个节点中的值的引用
back返回list的最后一个节点中的值的引用

list modifiers

函数说明接口介绍
push_back在list首元素之前插入值为val的元素(首插)
pop_front删除list首元素(首删)
push_back在list尾部插入值为val的元素(尾插)
pop_back删除list最后一个元素(尾删)
insert在list position位置插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

list迭代器失效

list的底层是双向循环链表,所以在list中进行插入操作时不会导致其迭代器失效;当对链表进行删除操作时,指向被删除节点的迭代器会失效,其他迭代器不受影响。

深度剖析模拟实现

模拟实现list

结构体实现链表节点
这里采用模板,因为节点中元素的类型可以由操作者决定

    //节点
	template<class T>
	struct list_node
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T _data;
   
        //初始化节点
		list_node(const T& x)
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};

模拟实现迭代器,list的迭代器不同于vector,string的迭代器;前者的物理空间是连续的,迭代器就类似原生指针,但后者的物理空间并不是连续的,每个节点之间是通过节点的指针连接在一起的,所以模拟实现迭代器的本质便是:模拟实现节点的指针

先实现简单的迭代器
节点中的指针的功能:读取节点中的元素,指向前一个节点和后一个节点,判断两个节点是否相等

    //迭代器
	template<class T>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T> iterator;
		
		node* _pnode;

		__list_iterator(node*p)
			:_pnode(p)
		{}

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

		iterator& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		iterator& operator++(int)
		{
			iterator tmp(*this);
			_pnode = _pnode->_next;
			return tmp;
		}

		iterator& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		iterator& operator--(int)
		{
			iterator tmp(*this);
			_pnode = _pnode->_prev;
			return tmp;
		}

		bool operator!=(const iterator& it)
		{
			return _pnode != it._pnode;
		}

		bool operator==(const iterator& it)
		{
			return _pnode == it._pnode;
		}
	};

模拟实现链表
基本框架在数据结构中都已学习过,这里不加赘述

//模拟实现list
	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T> iterator;
		iterator begin()
		{
			return iterator(_head->_next);
		}

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

		void empty_initialize()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		list()
		{
			empty_initialize();
		}

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

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

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

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

		size_t size()
		{
			return _size;
		}

		bool empty()
		{
			return _size == 0;
		}

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

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

		iterator insert(iterator pos, const T& x)
		{
			node* newnode = new node(x);
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			//prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;

			return iterator(newnode);
		}

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

		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		
		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			--_size;
			return iterator(pos);
		}

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

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

	private:
	    //链表中的头节点
		node* _head;
		//记录节点的个数
		size_t _size;
	};

当遇到const类型的数据时,程序便出现会出现问题

void Print_list(const list<int>& lt)
	{
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
		}
		cout << endl;
	}

	void test()
	{
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		Print_list(lt);
	}

在这里插入图片描述

这里出现问题的本质是权限扩大,没有实现const的迭代器
为了解决这个问题,模拟实现const迭代器,给const迭代器取名为:
const_iterator

   //const迭代器
	template<class T>
	struct __list_const_iterator
	{
		typedef list_node<T> node;

		node* _pnode;

		__list_const_iterator(node* p)
			:_pnode(p)
		{}

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

		__list_const_iterator<T>& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		__list_const_iterator<T>& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		bool operator!=(const __list_const_iterator<T>& it)const
		{
			return _pnode != it._pnode;
		}

		bool operator==(const __list_const_iterator<T>& it)
		{
			return _pnode == it._pnode;
		}
	};

程序正常运行

在这里插入图片描述

如果仔细观察普通迭代器和const迭代器会发现,两段代码的重复度较高,说明代码质量不高,下面通过加入一个模板参数的方式优化代码

    //优化之后的迭代器 Ref 代表 T&
    template<class T,class Ref>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref> Self;
		
		node* _pnode;

		__list_iterator(node*p)
			:_pnode(p)
		{}

		Ref operator*()
		{
			return _pnode->_data;
		}
		
		Self& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		Self& operator++(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;
		}

		Self& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		Self& operator--(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_prev;
			return tmp;
		}

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}

		bool operator==(const Self& it)
		{
			return _pnode == it._pnode;
		}
	};

const类型数据的问题已经解决,又出现另一个问题,既然链表中的元素类型没有限制,那么如果链表中元素的类型是一个坐标,该如何打印呢?

学习结构体时,结构体变量可以通过指针有选择地访问结构体中的变量,所以只需要在迭代器中重载指针操作即可。做法也很简单,在
此前模板两个参数的情况下,再添加上指针类型参数。

代码如下

T    参数类型
Ref  引用类型
Ptr  指针类型
template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> Self;
		
		node* _pnode;

		__list_iterator(node*p)
			:_pnode(p)
		{}

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

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

		Self& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		Self& operator++(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;
		}

		Self& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		Self& operator--(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_prev;
			return tmp;
		}

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}

		bool operator==(const Self& it)
		{
			return _pnode == it._pnode;
		}
	};

打印坐标如下

    struct Pos
 	{
		int _row;
		int _col;

		Pos(int row = 0,int col = 0)
			:_row(row)
			,_col(col)
		{}

	};

	void test1()
	{
		list<Pos> lt;
		lt.push_back(Pos(0, 0));
		lt.push_back(Pos(1, 1));
		lt.push_back(Pos(2, 2));
		list<Pos>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << it->_row << " " << it->_col << endl;
			++it;
		}
		cout << endl;
	}

在这里插入图片描述

迭代器实现完毕,接下来list的实现就很简单,不加赘述,代码如下

	//模拟实现list
	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

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

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

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

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

		void empty_initialize()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()
		{
			empty_initialize();
		}

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

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

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

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

		size_t size()
		{
			return _size;
		}

		bool empty()
		{
			return _size == 0;
		}

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

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

		iterator insert(iterator pos, const T& x)
		{
			node* newnode = new node(x);
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			//prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return iterator(newnode);
		}

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

		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		
		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			--_size;
			return iterator(pos);
		}

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

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

	private:
		node* _head;
		size_t _size;
	};

上面介绍的是正向迭代器,有正向迭代器肯定也有反向迭代器,先来了解反向迭代器的结构

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

根据上图,也就是说反向迭代器是将正向迭代器的 begin()作为 rend(), end()作为 rbegin();简单来说就是于正向迭代器相反。

但也存在着区别,当读取最后一个元素时,是直接进行解引用然后提取吗?由上图可知并不是,如果直接对rbegin()解引用,提取到的是头节点的数据,而不是最后一个节点的数据。所以当读取最后一个元素的正确操作是,rbegin()迭代器先向前移动一位,再进行解引用,便可提取到最后一个元素

代码实现如下

        //Ref类型的引用   T&
        Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}

反向迭代器的整体代码如下

    template<class Iterator,class Ref,class Ptr>
	class reverseIterator
	{
	public:
		reverseIteartor(Iterator it)
			:_it(it)
		{

		}

		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}

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

		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}

		bool operator!=(const Self& s)
		{
			return _it != s._it;
		}
	private:
		Iterator _it;
	};

list与vector对比

vectorlist
底层结构动态顺序表,连续空间带头双向循环链表
随机访问支持随机访问,效率O(1)不支持随机访问,效率O(N)
插入,删除任意位置插入删除,删除效率低,需要挪动元素,时间复杂度O(N),插入时有可能会增容:开辟新空间,拷贝元素,释放旧空间,效率低任意位置插入,删除效率高,不需要挪动元素,时间复杂度O(1)
空间利用率底层是连续的空间,不易造成内存碎片,空间利用率高底层节点动态开辟,小节点易造成内存碎片,空间利用率低
迭代器原生指针原生指针进行封装
迭代器失效插入元素时,重新给迭代器赋值,插入可能会导致扩容,导致迭代器失效;删除时,当前迭代器也需要重新赋值否则会失效插入元素不会导致迭代器失效;删除元素时只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值