【C++】List

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中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。
在这里插入图片描述
list iterator的使用:
在这里插入图片描述
【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

list capacity:
在这里插入图片描述
list element access:
在这里插入图片描述

List的测试


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

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

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

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

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.reverse();

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//lt.sort();
	lt.sort(greater<int>());
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.unique();

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

}

在这里插入图片描述

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

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.splice(lt.end(), lt, lt.begin());
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> lt1;
	lt1.push_back(10);
	lt1.push_back(20);
	lt1.push_back(30);
	lt1.push_back(40);
	/*lt.splice(lt.begin(),lt);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;*/

	lt.splice(lt.begin(), lt1);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;


	/*for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;*/


}

在这里插入图片描述

List的模拟实现

List的节点

template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			:_prev(nullptr)
			, _next(nullptr)
			, data(val)
		{

		}
		ListNode<T>* _prev;//为什么是ListNode<T>*的指针呢?因为我们将来要指向
		//ListNode<T>类型的节点。
		ListNode<T>* _next;
		T data;
	};

List的构建

双向带头链表,所以list一定要有头结点。

template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
	private:
		Node* _head;//头结点是一个节点指针类型。
		size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
	};

};

List的构造

list()
		{
			empty_init();
			//刚创建一个链表的时候,没有插入任何数据,我们需要让他指向自己
		}

尾插push_back:

void push_back(const T& val)
{
	Node* node = new Node(val);//申请节点
	Node* tail = _head->_prev;//如何找到4?是_head->prev!保存起来方便我们使用。
	node->_prev = tail;//链接
	node->_next = _head;
	tail->_next = node;
	_head->_prev = node;
	_size++;//修改size
}

尾删pop_back:

先找到尾节点,然后保存一起来(方便我们删除)。然后将指针链接到尾删的前一个值然后保存一起来(方便我们删除)。然后将指针链接到尾删的前一个值。

void pop_back()
{
	Node* tail = _head->_prev;//保存

	_head->_prev = tail->_prev;//更改指向
	tail->_prev->_next = _head;
	delete tail;//删除节点
	_size--;//别忘了修改个数
}


节点个数:size()

size_t size()
{
	return _size;
}

判断是否为空:empty()

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

迭代器:

template<class T>
class List_Iterator
{
public:
	typedef ListNode<T> Node;
	Node* _node;

	List_Iterator(Node* node)
		:_node(node)
	{}
};

begin(和)end()

begin()是返回数据的开始和end()返回头结点(有效数据的下一个)。

template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		iterator begin()
		{
			//这里存在单参数构造函数的隐式类型转换
			//按道理 return iterator(_head->next);
			//应该是这样的,匿名对象,但是单参数的构造函数可以进行隐式类型转换。
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
	private:
		Node* _head;//头结点是一个节点指针类型。
		size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
	};

operator*():

打印节点的数值。

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

前置++ operator++():

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

后置++ operator++(int):

后置++,就是我们创建一个临时变量,然后返回临时变量,但是让实际已经指向下一个节点了。注:不能返回引用,因为是临时变量。

List_Iterator<T> operator++(int)
{
	List_Iterator<T> tmp(*this);
	_node = _node->_next;
	return tmp;
}

前置-- operator–():

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

后置-- operator–(int)

List_Iterator<T> operator--(int)
{
	Self tmp(*this);
	_node = _node->_prev;
	return tmp;
}

重载不等于 operator!=(const Self& rhs):

bool operator!=(const Self& rhs )
{
	return rhs._node != _node;
}

重载箭头 operator->():

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

const迭代器

以上我们都是写的普通的迭代器,那么如果我们要写const的迭代器是要重新写一个。

template<class T>
class List_ConstIterator
{
public:
	typedef ListNode<T> Node;
	typedef List_ConstIterator<T> Self;
	Node* _node;

	List_ConstIterator(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;
	}
	bool operator!=(const Self& rhs)  
	{
		return rhs._node != _node;
	}
	bool operator==(const Self& rhs)
	{
		return _node == rhs._node;
	}

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

但是这个迭代器的代码和普通迭代器的代码有很多相似之处,所以我们会觉得代码冗余,那么我们是不是可以利用类模板写呢?事实证明是可以的。

template<class T ,class Ref ,class Ptr>
class List_Iterator
{
public:
	typedef ListNode<T> Node;
	typedef List_Iterator< T, Ref, Ptr> Self;
	Node* _node;

	List_Iterator(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;
	}
	bool operator!=(const Self& rhs )
	{
		return rhs._node != _node;
	}
	bool operator==(const Self& rhs)
	{
		return _node == rhs._node;
	}
	Ptr operator->()
	{
		return &_node->data;
	}
	Ref operator* ()
	{
		return _node->data;
	}
};

在某个位置前插入 insert():

在这里插入图片描述

iterator insert(iterator pos, const T& val)
		{
			 Node* node = new Node(val);
			 Node* prev=pos->prev;
			 node->prev=prev;
			 node->next=pos;
			 pos->prev=node;
			 prev->next=node;
			 _size++;
			 return _head
		}

最后记得节点的个数+1。

删除某位置的值 erase():

在这里插入图片描述
在这里插入图片描述
库中要求返回被删除节点的下一个迭代器。

iterator erase(iterator pos)
		{
			
		
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return next;

		}

清空链表 clear():

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

拷贝构造:

拷贝构造,由于你的头指针没有初始化,都是空,插入直接空指针访问,所以不能用push_back。所以你需要初始化。

//lt1(lt2)
list(const list<T>& lt)
{
	empty_init();//初始化头结点。
	for (auto& e : lt)
	{
		push_back(e);
	}
	iterator it = lt.begin();
	
}

析构函数:

~list()
{
	clear();//复用
	delete _head;
}

整体代码展现:

#include<iostream>
#include<assert.h>

namespace bit {
	template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			:_prev(nullptr)
			, _next(nullptr)
			, data(val)
		{

		}
		ListNode<T>* _prev;//为什么是ListNode<T>*的指针呢?因为我们将来要指向
		//ListNode<T>类型的节点。
		ListNode<T>* _next;
		T data;
	};
	template<class T, class Ref, class Ptr>
	class List_Iterator
	{
	public:
		typedef ListNode<T> Node;
		typedef List_Iterator< T, Ref, Ptr> Self;
		Node* _node;

		List_Iterator(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;
		}
		bool operator!=(const Self& rhs)
		{
			return rhs._node != _node;
		}
		Ref operator* ()
		{
			return _node->data;
		}
		bool operator==(const Self& rhs)
		{
			return _node == rhs._node;
		}
		Ptr operator->()
		{
			return &_node->data;
		}
	};
	//template<class T>
	//class List_ConstIterator
	//{
	//public:
	//	typedef ListNode<T> Node;
	//	typedef List_ConstIterator<T> Self;
	//	Node* _node;

	//	List_ConstIterator(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;
	//	}
	//	bool operator!=(const Self& rhs)  
	//	{
	//		return rhs._node != _node;
	//	}
	//	const T& operator* ()
	//	{
	//		return _node->data;
	//	}
	//	bool operator==(const Self& rhs)
	//	{
	//		return _node == rhs._node;
	//	}
	//	const T* operator->()
	//	{
	//		return &_node->data;
	//	}
	//};
	template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		typedef List_Iterator<T, T&, T*> iterator;
		typedef List_Iterator<T, const T&, const T*> const_iterator;
		//typedef List_Iterator<T> iterator;
		//typedef List_ConstIterator<T> const_iterator;
		void empty_init()
		{
			_head = new Node;//给头一个空间
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}
		list()
		{
			empty_init();
			//刚创建一个链表的时候,没有插入任何数据,我们需要让他指向自己
		}
		~list()
		{
			clear();
			delete _head;
		}
		//lt1(lt2)
		list(const list<T>& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
			iterator it = lt.begin();
			//类型不明确,用范围for更好
				//auto it = lt.begin();//这样也可以
				//while (it != lt.end())
				//{
				//	push_back(*it);
				//	it++;
				//}
		}
		void push_back(const T& val)
		{
			/*Node* node = new Node(val);
			Node* tail = _head->_prev;
			node->_prev = tail;
			node->_next = _head;
			tail->_next = node;
			_head->_prev = node;*/
			insert(end(), val);
		}
		void pop_back()
		{
			/*Node* tail = _head->_prev;

			_head->_prev = tail->_prev;
			tail->_prev->_next = _head;
			delete tail;*/
			erase(--end());
		}
		//当我们想使用insert的时候,发现需要迭代器那么我们先实现迭代器,怎么做呢?
		iterator begin()
		{
			//这里存在单参数构造函数的隐式类型转换
			//
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin()const
		{
			//这里存在单参数构造函数的隐式类型转换
			return _head->_next;
		}
		const_iterator end()const
		{
			return _head;
		}
		//目前测试的迭代器没有大碍,那么就写insert
		iterator insert(iterator pos, const T& val)
		{
			 Node* node = new Node(val);
			 Node* prev=pos->prev;
			 node->prev=prev;
			 node->next=pos;
			 pos->prev=node;
			 prev->next=node;
			 _size++;
			 return _head
		}
		iterator erase(iterator pos)
		{
			
		
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return next;

		}
		size_t size()
		{
			return _size;
		}
		bool empty()
		{
			return _size == 0;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			_size = 0;
		}
	private:
		Node* _head;
		size_t _size;
	};
}
  • 33
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lehjy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值