list详解&&list模拟

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、list

在这里插入图片描述
list是双向带头循环链表(doubly linked list),允许O(1)时间复杂度的插入和删除

1.list的遍历

list的遍历相比于string,vector,不能够随机访问,所以少了[ ]的遍历方式,只能用迭代器去遍历(范围for底层也是迭代器,所以这里算作同一种)

#include <list>
#include <iostream>
using namespace std;
int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	list<int>::iterator it = lt.begin();
	while(it!=lt.end())
	{
		cout<<*it<<" ";
		it++;
	}
	cout<<endl;
	return 0;
}

2.insert和erase

insert和erase需要使用到算法库当中的find
在这里插入图片描述
在这里插入图片描述

#include <list>
#include <iostream>
#include <algorithm>
int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(2);
	lt.push_back(3);
	list<int>::iterator pos = find(lt.begin(),lt.end(),2);
	lt.insert(pos,20);
	lt.insert(pos,30);
	//遍历
	return 0;
}

在这里插入图片描述

#include <list>
#include <iostream>
#include <algorithm>
int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(2);
	lt.push_back(3);
	list<int>::iterator pos = find(lt.begin(),lt.end(),2);
	lt.erase(pos);
	pos=find(lt.begin(),lt.end(),2);
	lt.erase(pos);
	//遍历
	return 0;
}

关于insert和erase的迭代器失效问题:
insert是不会失效的,因为insert是在之前插入数据,指针存储的地址并没有变化,而erase,当前位置的地址已经回收了,所以erase迭代器失效,但是insert和erase都会返回一个迭代器,所以我们都可以把不失效的情况也当作失效,然后用it = erase(…)来更新迭代器,就不会出现迭代器失效的问题了。顺便提一下,像vector的迭代器失效问题,是数据前挪和后挪导致的,所以我们想要更新迭代器才行

2.Operations相关函数

在这里插入图片描述

1.splice的作用是转移,转移后当前链表中的数据就没了
2.remove删除特定值的所有结点(多次find+erase)
3.remove_if删除满足条件的所有结点,需要配合仿函数
4.unique 作用是去重
5.merge 将两个链表进行归并
6.sort,配合仿函数使用,默认是升序
7.reverse逆置(前后两个指针,交换数据即可)

算法库中的sort是使用的是快排,针对的是连续空间,所以list需要自己写一个独立的出来,链表的sort底层就是归并排序。(也可以用插入排序,冒泡排序等等)
但是,链表的sort价值不大,不如使用vector+算法库的sort。

二、list模拟

迭代器可以用一个独立的类来写,可以写成外部类或者内部类(C++中struct以及升级成了类,C++不喜欢使用内部类)

namespace zhupi
{
	template<class T>
	struct list_node//单个结点的构造
	{
		T _data;
		list_node* _next;
		list_node* _prev;
		list_node(const T& x = T())
			:_data(x)
			,_next(nullptr)
			,_prev(nullptr)
		{}	
	};
	
	template<class T,class Ref,class Ptr>
	struct 	__list_iterator//迭代器
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T>  iterator;
		Node*_node;
		//list的迭代器其实就是对指针的封装,对指针的操作都再迭代器中完成的
		__list_iterator()
		{}//空有一个_node指针,反正最后都要给他赋值
		__list_iterator(Node* x)
			:_node(x)
		{}
		__list_iterator(const iterator& it)
			:_node(it._node)
		{}
		
		iterator& operator++()//前置++
		{
			_node=_node->_next;
			return *this;
		}
		iterator& operator++(int)//后置++
		{
			iterator tmp = _node->_next;
			_node=_node->_next;
			return tmp;
		}
		iterator& operator--()
		{
			_node=_node->_prev;
			return *this;
		}
		iterator& operator--(int)//后置
		{
			iterator tmp = _node->——prev;
			_node=_node->_prev;
			return tmp;
		}
		list<T>& operator=(list<T> lt)//注意参数
		{
			swap(lt);//交换_head即可
			return *this;
		}
		bool operator==(const iterator& it)
		{
			return _node==it._node;
		}
		bool operator!=(const iterator& it)
		{
			return !(_node==it._node);
		}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			//这里的解引用是为了让iterator变成Node*
			return &(operator*());
		}
		
	};

	template<class T>	
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T,,T&,T*> iterator;
		typedef __list_iterator<const T,const T&,const T*> const_iterator;
		
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin()
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()
		{
			return const_iterator(_head);
		}
		
		void empty_init()
		{
			_head=new Node;
			_head -> _next =  _head;
			_head -> _prev =  _head;
		}
		
		list()
		{
			empty_init();
		}
		template< class InputIterator>
		list(InputIterator first,InputIterator last)
		{
			empty_init();//创建并初始化
			while (first != last)
			{
				push_back(first->_data);
				++first;
			}
		}
		void swap(list<T>& x)
		{
			std::swap(_head,x._head);
		}
		//拷贝构造,深拷贝,复用迭代器区间构造
		list(const list<T>& lt)
		{
			empty_init();//创建并初始化
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}
		
		~list()
		{
			clear();
			delete _head;
			_head=nullptr;
		}
		
		void clear()
		{
			iterator it = begin()
			while(it!=end())
			{
				it = erase(it);
//				erase会返回下一个结点的迭代器,不用手动++
			}
		}
		

		push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node*tail = _head->_prev;
			tail->_next=newnode;
			newnode->_prev = tail;
			newnode->_next=_head;
			_head->prev=newnode;
		}
		
		void push_front(const T& x)
		{
			insert(begin(),x);
		}
		
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
	 	}

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

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			delete cur;
			prev->_next = next;
			next->_prev = prev;
			return iterator(next);
		}
	private:
		Node* _head;
	}
}

1.operator->()的省略

//Pos是一个结构体,有成员变量a1和a2
Pos aa;
list<Pos> lt;
lt.push_back(Pos(10,20));//坐标
lt.push_back(Pos(10,21));
list<Pos>::iterator it = lt.begin()
while(it!=lt.end())
{
	cout<<(*it)._a1<<endl;
	//明显it->_a1更好
}

operator->的目的是将iterator转换成Node*,只有转换成了Node*才能去->
本来应该是这样的

cout<<it->->_a1<<endl;
//cout<<it.operator->()->_a1;

编译器为了可读性进行了特殊处理

2.list反向迭代器(迭代器适配器)

迭代器适配器的设计是
正向迭代器的begin的位置是反向迭代器rend的位置
正向迭代器的end的位置是反向迭代器rbegin的位置

使用的时候就让rbegin访问_head->_prev,让rend访问_head,而且这样的设计也是为了让正向迭代器和反向迭代器完全对称,完全对称也有其他的好处

对上面list的添加

temlate<class iterator,class Ref,class Ptr>
struct __reverse_iterator
{
	Iterator _cur;
	typedef __reverse_iterator<Iterator,Ref,Ptr> RIterator;
	__reverse_iterator(){};
	__reverse_iterator(Iterator it)
		:_cur(it)
	{}

	RIterator operator++()
	{
		--_cur;//Iterator的--复用
		return *this
	}
	RIterator operator--()
	{
		++_cur;//Iterator的++复用
		return *this
	}
	Ref operator*()
	{
		auto tmp=_cur;
		return *--tmp;
		//取得的是前一个的数据
		//因为为了正反向迭代器位置的对称设计,只能这样去访问
	}
	Ptr operator->()
	{
		return &(operator*());
	}
	bool operator!=(const RIterator& it)
	{
		return _cur!=it._cur;
	}
}

反向迭代器又叫做迭代器适配器,所谓适配器,就要尽可能的去复用。
任何一个容器只要实现了正向迭代器(按规范),就能适配出反向迭代器

3.迭代器按功能分类

迭代器按功能分为
forward_iterator单向迭代器
bidirectional_iterator双向迭代器
random_access_iterator随机迭代器

单向迭代器处理的是适合++操作的容器,不适合–;
双向迭代器适合++和–操作的容器
随机迭代器适用于++,–,+,-操作的容器(比如指针+8指向的地址)

在这里插入图片描述
用名称来暗示sort的容器的迭代器是随机迭代器,用sort来排序链表就崩了
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值