C++list

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的构造

构造函数( (constructor))接口说明
list()构造空的list
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list
// constructing lists
#include <iostream>
#include <list>
int main ()
{
    std::list<int> l1; // 构造空的l1
    std::list<int> l2 (4,100); // l2中放4个值为100的元素
    std::list<int> l3 (l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
    std::list<int> l4 (l3); // 用l3拷贝构造l4
    
    // 以数组为迭代器区间构造l5
    int array[] = {16,2,77,29};
    std::list<int> l5 (array, array + sizeof(array) / sizeof(int) );//原生指针可以当成天然的迭代器使用
    
    // 用迭代器方式打印l5中的元素
    for(std::list<int>::iterator it = l5.begin(); it != l5.end(); it++)
    std::cout << *it << " ";
    std::cout<<endl;
    
    // C++11范围for的方式遍历
    for(auto& e : l5)
    std::cout<< e << " ";
    std::cout<<endl;
    
    return 0;
}

list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

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

【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
#include <iostream>
using namespace std;
#include <list>
void print_list(const list<int>& l)
{
    // 注意这里调用的是list的 begin() const,返回list的const_iterator对象
    for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
    {
        cout << *it << " ";
        // *it = 10; 编译不通过
    }
    
    cout << endl;
    }

int main()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    //用array数组构造
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    
    // 使用正向迭代器正向list中的元素
    for (list<int>::iterator it = l.begin(); it != l.end(); ++it)
    	cout << *it << " ";
    
    cout << endl;
    
    // 使用反向迭代器逆向打印list中的元素
    for (list<int>::reverse_iterator it = l.rbegin(); it != l.rend(); ++it)
    	cout << *it << " ";
    
    cout << endl;
    
    return 0;
}

list capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

list element access

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

list sort

算法库中提供了一个sort接口,通过传迭代器实现,但sort不能对list进行排序,因为sort的底层是快排,要求排序的区间是能够被随机访问的(比如vector可以通过[]随机访问)

list本身提供了一个sort接口,但不建议使用。

list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素
#include <list>
void PrintList(list<int>& l)
{
    for (auto& e : l)
        cout << e << " ";
    
    cout << endl;
}

//=====================================================================================
====
    
// push_back/pop_back/push_front/pop_front
void TestList1()
{
    int array[] = { 1, 2, 3 };
    list<int> L(array, array+sizeof(array)/sizeof(array[0]));
    
    // 在list的尾部插入4,头部插入0
    L.push_back(4);
    L.push_front(0);
    PrintList(L);
    
    // 删除list尾部节点和头部节点
    L.pop_back();
    L.pop_front();
    PrintList(L);
}

//=====================================================================================
====
    
// insert /erase
void TestList3()
{
    int array1[] = { 1, 2, 3 };
    list<int> L(array1, array1+sizeof(array1)/sizeof(array1[0]));
    
    // 获取链表中第二个节点
    auto pos = ++L.begin();
    cout << *pos << endl;
    
    // 在pos前插入值为4的元素
    L.insert(pos, 4);
    PrintList(L);
    
    // 在pos前插入5个值为5的元素
    L.insert(pos, 5, 5);
    PrintList(L);
    
    // 在pos前插入[v.begin(), v.end)区间中的元素
    vector<int> v{ 7, 8, 9 };
    L.insert(pos, v.begin(), v.end());
    PrintList(L);
    
    // 删除pos位置上的元素
    L.erase(pos);
    PrintList(L);
    
    // 删除list中[begin, end)区间中的元素,即删除list中的所有元素
    L.erase(L.begin(), L.end());
    PrintList(L);
}
// resize/swap/clear
void TestList4()
{
    // 用数组来构造list
    int array1[] = { 1, 2, 3 };
    list<int> l1(array1, array1+sizeof(array1)/sizeof(array1[0]));
    PrintList(l1);
    
    // 交换l1和l2中的元素
    l1.swap(l2);
    PrintList(l1);
    PrintList(l2);
    
    // 将l2中的元素清空
    l2.clear();//清空只是将size变成0,并不会清空list本身的结构
    
    cout<<l2.size()<<endl;
}

list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节
点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入(insert)时是不会导致list的迭代
器失效的,只有在删除(erase)时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

void TestListIterator1()
{
 int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
 list<int> l(array, array+sizeof(array)/sizeof(array[0]));

 auto it = l.begin();

 while (it != l.end())
 {
     // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
     l.erase(it);
     ++it;
 }
 }
 // 改正
void TestListIterator()
{
 int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
 list<int> l(array, array+sizeof(array)/sizeof(array[0]));

 auto it = l.begin();

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

list的模拟实现

模拟实现list

要模拟实现list,必须要熟悉list的底层结构以及其接口的含义,通过上面的学习,这些内容已基本掌握,现
在我们来模拟实现list

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace ysj
{ 

	class Date {
	public:
		int _year = 0;
		int _month = 1;
		int _day = 1;

	public:
		Date()
		{
			;
		}
	};

	//节点
	template<class T>
	struct _listnode {

		//构造
		_listnode(T val = T())
			:_val(val)
			, _next(nullptr)
			, _prev(nullptr)
		{}

		T _val;
		_listnode<T>* _next;
		_listnode<T>* _prev;
	};

	//迭代器
	//迭代器的拷贝构造、赋值重载、析构用编译器自己默认生成的就行,不需要考虑深拷贝问题
	template <class T, class Ref, class Ptr>
	struct _list_iterator {
		typedef _listnode<T> node;
		typedef _list_iterator<T, Ref, Ptr> self;
		node* _pnode;

		//构造
		_list_iterator(node*pnode = nullptr)
			:_pnode(pnode)
		{}

		/*_list_iterator(const self& lt)
			:_pnode(lt._pnode)
		{}*/

		/*_list_iterator(T* pval= nullptr)
		{
			_pnode = new node;

			_pnode->_val = *pval;

		}*/

		//解引用*的重载,返回引用
		Ref operator*()
		{
			return _pnode->_val;
		}

		//->的重载
		Ptr operator->()
		{
			return &_pnode->_val;
		}


		//!=的重载
		bool operator!=(const self& s)const
		{
			return _pnode != s._pnode;
		}

		//==的重载
		bool operator==(const self& s)const
		{
			return !(_pnode != s._pnode);
		}

		//前置++的重载
		self& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		//后置++的重载
		self& operator++(int)
		{
			//node* tmp = _pnode;
			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;
		}


	};

	//链表
	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;
	private:
		node* _head;

	public:
		//构造
		list(T val = T())
		{
			_head = new node(val);
			_head->_next = _head;
			_head->_prev = _head;
		}

		list(int num, T val)
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			while (num--)
			{
				push_back(val);
			}
		}

		//迭代器构造
		template <class Iterator>//用模板接收迭代器,利用迭代器++可以访问容器的特点
		list(Iterator left, Iterator right)
		{
			_head = new node;
			_head->_prev = _head;
			_head->_next = _head;

			while (left != right)
			{
				push_back(*left);
				++left;
			}
		}

		//拷贝构造
		list(const list<T>& lt)
		{
			//先初始化
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

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

		//赋值重载
		//传统写法
		//const list<T>& operator=(const list<T>& lt)
		//{
		//	//不能自己给自己赋值
		//	if (this != &lt)
		//	{
		//		//先把自己clear
		//		clear();
		//		
		//		//再逐个赋值
		//		for (const auto& e : lt)
		//		{
		//			push_back(e);
		//		}

		//	}

		//	return *this;
		//}

		//现代写法
		const list<T>& operator=(list<T> lt)
		{
			//lt是形参,函数返回即销毁,交换两个head的指向,让lt去帮忙销毁本来的_head
			if (this != &lt)
			swap(lt._head, _head);

			return *this;
		}

		//迭代器
		iterator begin()
		{
			return iterator(_head->_next);
		}

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

		//const迭代器
		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}

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

		//判空
		bool empty()
		{
			return begin() == end();
		}

		//返回链表数据个数,如果要频繁使用数据的个数,可以定义一个size成员变量,频繁调用函数是有消耗的
		size_t size()const
		{
			size_t count = 0;
			list<T>::const_iterator it = begin();

			while (it != end())
			{
				++count;
				++it;
			}

			return count;
		}


		//插入,在pos之前插入
		void insert( iterator pos, const T& val)
		{
			//先创建出新节点
			node* newnode = new node(val);

			//调整前后对应关系
			node* prev = pos._pnode->_prev;
			node* cur = pos._pnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			newnode->_next = cur;
			cur->_prev = newnode;


		}

		//头插
		void push_front(const T& val)
		{
			//复用insert
			insert( begin(), val);
		}

		//尾插
		void push_back(const T& val)
		{
			//可以复用insert
			insert( end(), val);//插在最后一个位置也就是在end之前插入

			/*node* newnode = new node(val);

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

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

		//删除
		iterator erase(iterator pos)
		{
			assert(!empty());

			node* prev = pos._pnode->_prev;
			node* next = pos._pnode->_next;

			//将pos位置的前后节点连接
			prev->_next = next;
			next->_prev = prev;

			return next;
		}

		//头删
		void pop_front()
		{
			assert(_head->_next != _head);

			//复用erase代码
			erase(begin());
		}

		//尾删
		void pop_back()
		{
			assert(_head->_next != _head);

			//复用erase
			erase(--end());

			/*node* newtail = _head->_prev->_prev;

			newtail->_next = _head;
			_head->_prev = newtail;*/
		}

		void clear()
		{
			list<T>::iterator it = begin();

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

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


	};

关于->的重载

//->的重载
		Ptr operator->()
		{
			return &_pnode->_val;
		}

//测试
//测试->重载
	void TestList3()
	{
		ysj::list<Date> l1;
		l1.push_back(Date());
		l1.push_back(Date());
		l1.push_back(Date());
		l1.push_back(Date());
		l1.push_back(Date());

		auto it = l1.begin();
		while (it != l1.end())
		{
			cout << ((*it)._year) << ((*it)._month) << ((*it)._day) << endl;
			it++;
		}
		cout << endl;

		it = l1.begin();
		while (it != l1.end())
		{
			cout << (it->_year) << (it->_month) << (it->_day) << endl;
			it++;
		}
		cout << endl;

	}

获取_val的成员变量:it-> _year,本来应该是it->-> _year

it->返回的是_val的地址,应该对该地址再使用一次->,才能够访问到 _val的成员,但编译器在这里做了优化,为了代码的可读性,只需要一个->就可以访问到 _val的成员。

对模拟的list进行测试

	// 正向打印链表
	template<class T>
	void PrintList(const ysj::list<T>& l)
	{
		auto it = l.begin();
		while (it != l.end())
		{
			cout << *it << " ";
			++it;
		}

		cout << endl;
	}

	// 测试List的构造
	void TestList1()
	{
		ysj::list<int> l1;
		ysj::list<int> l2(10, 5);
		PrintList(l2);

		int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
		ysj::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
		PrintList(l3);

		ysj::list<int> l4(l3);
		PrintList(l4);

		l1 = l4;
		PrintList(l1);
		//PrintListReverse(l1);
	}

	// PushBack()/PopBack()/PushFront()/PopFront()
	void TestList2()
	{
		// 测试PushBack与PopBack
		ysj::list<int> l;
		l.push_back(1);
		l.push_back(2);
		l.push_back(3);

		PrintList(l);

		l.pop_back();
		l.pop_back();

		PrintList(l);

		l.pop_back();
		cout << l.size() << endl;

		// 测试PushFront与PopFront
		l.push_front(1);
		l.push_front(2);
		l.push_front(3);

		PrintList(l);

		l.pop_front();
		l.pop_front();

		PrintList(l);

		l.pop_front();
		cout << l.size() << endl;
	}
	void TestList3()
	{
		int array[] = { 1, 2, 3, 4, 5 };
		ysj::list<int> l(array, array + sizeof(array) / sizeof(array[0]));

		auto pos = l.begin();//指向1
		l.insert(l.begin(), 0);//在1之前插入0
		PrintList(l);

		++pos;//原来指向1,++之后指向2
		l.insert(pos, 2);//在2之前插入2
		PrintList(l);

		l.erase(l.begin());//删除0
		l.erase(pos);//删除第二个2
		PrintList(l);

		// pos指向的节点已经被删除,pos迭代器失效
		cout << *pos << endl;
		auto it = l.begin();

		while (it != l.end())
		{
			it = l.erase(it);
		}
		cout << l.size() << endl;
	}
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WoLannnnn

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

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

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

打赏作者

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

抵扣说明:

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

余额充值