【STL】list

l i s t list list C C C++ 标准模板库( S T L STL STL)中的一个序列容器 S e q u e n c e   C o n t a i n e r Sequence\ Container Sequence Container),它允许在容器的任意位置快速插入和删除元素,是一个能够存放任意类型双向带头循环链表。与数组或向量( v e c t o r vector vector)不同, l i s t list list 不需要在创建时指定大小,并且在任何位置添加或删除元素的效率为 O ( 1 ) O(1) O(1),而不需要重新分配内存。


前言 —— 迭代器

1. 迭代器的分类

迭代器根据各个容器底层结构决定可以使用哪些算法)不同,虽然所实现的功能相同,但是性质不同:迭代器只能够进行 ++ 操作,像++begin()/begin()++的就是单向迭代器;迭代器只能进行 ++ / / / - - 操作,像++begin()/--begin()的是双向迭代器;而迭代器能够随机访问底层是连续空间)操作的,像++begin()/--begin()/begin()+i/end()-i,是随机迭代器

在这里插入图片描述

C C C++ 官方文档中,我们看到 l i s t list list 的迭代器是一个双向的迭代器 b i d i r e c t i o n a l   i t e r a t o r bidirectional\ iterator bidirectional iterator):

在这里插入图片描述

其他的像 v e c t o r vector vector 是一个随机访问迭代器 r a n d o m   a c c e s s   i t e r a t o r random\ access\ iterator random access iterator):

在这里插入图片描述

u n o r d e r e d _ m a p unordered\_map unordered_map 是一个单向迭代器 f o r w a r d _ i t e r a t o r forward\_iterator forward_iterator):

在这里插入图片描述

2. 不同性质迭代器的区别

容器的底层结构决定了迭代器的性质,也决定了这些容器能使用哪些算法。

比如说排序算法 s o r t sort sort

在这里插入图片描述

可以看出,看似 s o r t sort sort 是一个函数模板支持所有类型,实际上其只支持随机访问迭代器的容器使用。

再比如逆置算法 r e v e r s e reverse reverse

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

可以看出,由于其使用到了 - - 迭代器的操作,因此 r e v e r s e reverse reverse 只支持双向迭代器的容器使用。

有些算法像 f i n d find find,还会出现 I n p u t I t e r a t o r InputIterator InputIterator 的概念:

在这里插入图片描述

这其实就代表着 f i n d find find 函数支持所有类型的迭代器。

实际上,单向迭代器、双向迭代器和随机访问迭代器属于继承关系,即包含关系:

在这里插入图片描述

总结:

也就是说,单向( F o r w a r d Forward Forward)迭代能支持的算法,双向( B i d i r e c t i o n a l Bidirectional Bidirectional)迭代器也支持;双向( B i d i r e c t i o n a l Bidirectional Bidirectional)迭代器支持的算法,随机访问( R a n d o m   A c c e s s Random\ Access Random Access)迭代器也支持;如果是支持 I n p u t I t e r a t o r InputIterator InputIterator 的算法,那么所有迭代器(容器)都支持。


一、list 的介绍

相较于 v e c t o r vector vector 的 连续线性空间, l i s t list list 就显得复杂很多,它的好处是:每次插入或删除一个元素,就配置或释放一个元素空间。而且,对于任何位置的元素插入或删除都是常数时间( O ( 1 ) O(1) O(1))。

因此, l i s t list list 对于空间的运用有绝对的精准,一点也不会浪费。

1. 结点(node)

每一个设计过 l i s t list list 的人都知道, l i s t list list 本身和 l i s t list list 的结点是不同的结构,需要分开设计。以下是 S T L STL STL l i s t list list 结点( n o d e node node)结构:

template<class T>
struct __list_node
{
	typedef void* void_pointer;
	void_pointer prev;	// 型别为 void*。其实可设为 __list_node<T>*
	void_pointer next;
	T data;
};

显然这是一个双向链表

2. 迭代器

l i s t list list 不再能够像 v e c t o r vector vector 一样以普通指针作为迭代器,因为其结点不保证在存储空间中连续存在 l i s t list list 迭代器必须有能力指向 l i s t list list 的结点,并有能力进行正确的递增递减取值成员存取等操作。

由于 S T L STL STL l i s t list list 是一个双向链表 d o u b l e   l i n k e d − l i s t double\ linked-list double linkedlist),迭代器必须具备前移后移的能力,所以 l i s t list list 提供的是 B i d i r e c t i o n a l   i t e r a t o r s Bidirectional\ iterators Bidirectional iterators

以下是 l i s t list list 迭代器的设计:

template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef __list_iterator<T, T&, T*> 		iterator;
	typedef __list_iterator<T, Ref, Ptr>	self;

	typedef bidrectional_iterator_tag iterator_category;
	typedef T value_type;
	typedef Ptr pointer;
	typedef Ref reference;
	typedef __list_node<T>* link_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	
	link_type node;	// 迭代器内部当然要有一个普通指针,指向 list 的结点
	
	// constructor
	__list_iterator(link_type x) : node(x) {}
	__list_iterator() {}
	__list_iterator(const iterator& x) : node(x.node) {}

	bool operator == (const self& x) const { return node == x.node; }
	bool operator != (const self& x) const ( return node != x.node; )
	
	// 以下对迭代器取值(dereference),取的是结点的数据值
	reference operator* () const { return (*node).data; }

	// 以下是迭代器成员存取(member access)运算子的标准做法
	pointer operator -> const { return &(operator * ()); }

	// 对迭代器累加 1,就是前进一个结点
	self& operator ++ ()
	{
		node = (link_type)((*node).next);
		return *this;
	}
	self operator ++ (int)
	{
		self tmp = *this;
		++*this;
		return tmp;
	}

	// 对迭代器递减 1,就是后退一个结点
	self& operator -- ()
	{
		node = (link_type)((*node).prev);
		return *this;
	}
	self operator -- (int)
	{
		self tmp = *this;
		--*this;
		return tmp;
	}
};

3. 数据结构

S G I SGI SGI l i s t list list 不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,便可完整表现整个链表:

template<class T, class Alloc = alloc>	// 缺省使用 alloc 为配置器
class list
{
protected:
	typedef __list_node<T> list_node;
public:
	typedef list_node* link_type;

protected:
	link_type node; // 只要一个指针,便可表示整个环状双向链表
...
};

如果让指针 n o d e node node 指向刻意置于尾端的一个空白节点 n o d e node node 便能符合 S T L STL STL 对于 “ 前闭后开 ” 区间的要求,成为 l a s t last last 迭代器,如下图所示。这么一来,以下几个函数便都可以轻易完成:

iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }

bool empty() const { return node->next == node; }
size_type size() const
{
	size_type result = 0;
	distance(begin(), end(), result);	// 全局函数
	return result;
}

// 取头结点的内容(元素值)
reference front() { return *begin(); }
// 取尾结点的内容(元素值)
reference back() { return *(--end()); }

在这里插入图片描述

关于更多 l i s t list list 的介绍,还是要多查官方文档: l i s t list list的文档介绍

在这里插入图片描述


二、list 的使用(常用接口)

下面只列举了 l i s t list list 最常用的几个接口,更多详细信息可以自行查官方文档: l i s t list list

l i s t list list 也采用了模板: t e m p l a t e < c l a s s   T ,   c l a s s   A l l o c = a l l o c a t o r < T > > c l a s s   l i s t ; template < class\ T,\ class\ Alloc = allocator<T> > class\ list; template<class T, class Alloc=allocator<T>>class list;

1. 常见构造

构造函数( c o n s t r u c t o r constructor constructor接口说明
l i s t ( s i z e _ t y p e   n ,   c o n s t   v a l u e _ t y p e &   v a l = v a l u e _ t y p e ( ) ) list(size\_type\ n,\ const\ value\_type\&\ val = value\_type()) list(size_type n, const value_type& val=value_type())构造的 l i s t list list 中包含 n n n 个值为 v a l val val 的元素
l i s t ( ) list() list()构造空的 l i s t list list
l i s t ( c o n s t   l i s t &   x ) list(const\ list\&\ x) list(const list& x)拷贝构造函数
l i s t ( I n p u t I t e r a t o r   f i r s t , I n p u t I t e r a t o r   l a s t ) list(InputIterator\ first, InputIterator\ last) list(InputIterator first,InputIterator last) [ f i r s t , l a s t ) [first, last) [first,last) 区间中的元素构造 l i s t list list

2. iterator 的使用

此处,可以暂时将迭代器理解成一个指针,该指针指向 l i s t list list 中的某个结点

函数声明接口说明
b e g i n begin begin + + + e n d end end返回第一个元素的迭代器 + + + 返回最后一个元素下一个位置的迭代器
r b e g i n rbegin rbegin + + + r e n d rend rend返回第一个元素的反向迭代器,即 e n d end end 位置,返回最后一个元素下一个位置的反向迭代器,即 b e g i n begin begin 位置

在这里插入图片描述
【注意】

  1. b e g i n begin begin e n d end end正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动

  2. r b e g i n ( e n d ) rbegin(end) rbegin(end) r e n d ( b e g i n ) rend(begin) rend(begin)反向迭代器,对迭代器执行 ++ 操作,迭代器向前移动

3. capacity

函数声明接口说明
e m p t y empty empty检测 l i s t list list 是否为空,是返回 t r u e true true,否则返回 f a l s e false false
s i z e size size返回 l i s t list list 中有效节点的个数

4. element access

函数声明接口说明
f r o n t front front返回 l i s t list list 的第一个节点中值的引用
b a c k back back返回 l i s t list list 的最后一个节点中值的引用

5. modifiers

函数声明接口说明
p u s h _ f r o n t push\_front push_front l i s t list list 首元素前插入值为 v a l val val 的元素
p o p _ f r o n t pop\_front pop_front删除 l i s t list list 中第一个元素
p u s h _ b a c k push\_back push_back l i s t list list 尾部插入值为 v a l val val 的元素
p o p _ b a c k pop\_back pop_back删除 l i s t list list 中最后一个元素
i n s e r t insert insert l i s t   p o s i t i o n list\ position list position 位置中插入值为 v a l val val 的元素
e r a s e erase erase删除 l i s t   p o s i t i o n list\ position list position 位置的元素
s w a p swap swap交换两个 l i s t list list 中的元素
c l e a r clear clear清空 l i s t list list 中的有效元素

6. operations

函数声明接口说明
s p l i c e splice splice将元素从 x x x l i s t list list) 转移到容器中,并将它们插入到 p o s pos pos 位置(剪切)
r e m o v e remove remove删除 l i s t list list 中值为 v a l val val 的元素
u n i q u e unique unique删除 l i s t list list 中重复的元素(先排序再去重)
m e r g e merge merge合并有序的多个 l i s t list list
s o r t sort sort对容器中的元素进行排序
r e v e r s e reverse reverse反转元素的顺序

三、list 的迭代器失效问题

之前把迭代器暂时理解成类似于指针迭代器失效迭代器所指向的节点的无效,即该节点被删除了。因为 l i s t list list底层结构带头结点的双向循环链表,因此在 l i s t list list 中进行插入时是不会导致 l i s t list list 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

1. 插入时不失效(不挪动数据):

void test1()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	
	auto it = l.begin();
	l.insert(it, 10);
	
	*it = 99;
	
	for (const auto& i : l)	cout << i << " "; cout << endl;
}

在这里插入图片描述

2. 删除时失效:

void test2()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	
	//删除所有偶数
	auto it = l.begin();
	while(it != l.end())
	{
		if (*it % 2 == 0)
		{
			l.erase(it);
		}
		++it;
	}
	
	for (const auto& i : l)	cout << i << " "; cout << endl;
}

在这里插入图片描述

3. 解决方案 —— —— —— 接收 e r a s e erase erase 返回值,返回的是下一个结点的迭代器:

void test3()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	
	//删除所有偶数
	auto it = l.begin();
	while(it != l.end())
	{
		if (*it % 2 == 0)
		{
			//需要更新一下it
			it = l.erase(it);
		}
		else
		{
			++it;
		}
	}
	
	for (const auto& i : l)	cout << i << " "; cout << endl;
}

在这里插入图片描述


四、list 与 vector 的对比

v e c t o r vector vector l i s t list list 都是 S T L STL STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

v e c t o r vector vector l i s t list list
底层结构动态顺序表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率 O ( 1 ) O(1) O(1)不支持随机访问,访问某个元素效率 O ( N ) O(N) O(N)
插入和删除任意位置插入和删除效率低,需要搬移元素,实现复杂度为 O ( N ) O(N) O(N),插入时有可能需要扩容:开辟新空间,拷贝元素,释放就空间,导致效率更低任意位置插入删除效率高,不需要搬移元素,时间复杂度为 O ( 1 ) O(1) O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层结点动态开辟,小结点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针(结点指针)进行封装
迭代器失效在插入元素时,要给所有迭代器重新赋值,因为插入元素有可能会导致重新扩容,导致原来的迭代器失效;删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关系插入删除效率大量插入和删除操作,不关心随机访问

五、list 的模拟实现

1. 模拟实现 list

根据之前的经验,由于 l i s t list list 也属于 C C C++ S T L STL STL 容器模板类),因此, l i s t list list 声明和定义都要写到一个文件中。这里不同的是: l i s t list list迭代器要自己封装实现(不能用原生态指针了),而且每个结点也是一个独立的结构要单独封装,因此,类外还要定义两个结构体:一个用来封装结点,另一个用来封装迭代器

因此,总共分为两个文件: l i s t . h list.h list.h 用来存放 l i s t list list 的定义和实现; t e s t . c p p test.cpp test.cpp 用来测试。

注意:这里两个文件中的 l i s t list list 都是自己定义的,为了和 s t d : : l i s t std::list std::list 作区分,我们要单独创建一个命名空间,这里我用的是 n a m e s p a c e   y b c namespace\ ybc namespace ybc

  1. l i s t . h list.h list.h
#pragma once
#include<iostream>
#include<list>
#include<assert.h> 

using namespace std;

namespace ybc
{
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

		list_node(const T& data = T())	//缺省参数要给匿名对象
			:_data(data)
			, _prev(nullptr)
			, _next(nullptr)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef list_node<T> node;
		typedef list_iterator<T, Ref, Ptr> self;
		node* _node;

		list_iterator(node* node)
			:_node(node)
		{}

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

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

		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& it) const
		{
			return _node == it._node;
		}

		bool operator != (const self& it) const
		{
			return _node != it._node;
		}
	};

	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;

		iterator begin()
		{
			/*iterator it(_head->_next);
			return it;*/

			/*return iterator(_head->_next);	//返回匿名对象*/

			return _head->_next;	//类型转换
		}

		iterator end()
		{
			/*iterator it(_head);
			return it;*/

			/*return iterator(_head);	//返回匿名对象*/

			return _head;	//类型转换
		}

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

		const_iterator end() const
		{
			return _head;
		}

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

		list()
		{
			empty_init();
		}

		list(const list<T>& l)
		{
			empty_init();
			for (auto& i : l)
			{
				push_back(i);
			}
		}

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

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

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

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

		iterator insert(iterator pos, const T& x)
		{
			node* cur = pos._node->_prev;
			node* newnode = new node(x);

			cur->_next->_prev = newnode;
			newnode->_prev = cur;
			newnode->_next = cur->_next;
			cur->_next = newnode;

			++_size;
			return newnode;
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

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

		iterator erase(iterator pos)
		{
			assert(pos != end());

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

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			--_size;
			return next;
		}

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

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

		size_t size() const
		{
			return _size;
		}

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

		T& front()
		{
			return _head->_next->_data;
		}

		const T& front() const
		{
			return _head->_next->_data;
		}

		T& back()
		{
			return _head->_prev->_data;
		}

		const T& back() const
		{
			return _head->_prev->_data;
		}

	private:
		node* _head;
		size_t _size = 0;
	};
}
  1. t e s t . c p p test.cpp test.cpp
#include"list.h"

namespace ybc
{
	template<class Container>
	void print(const Container& l)
	{
		for (const auto& i : l)
		{
			cout << i << " ";
		}
		cout << endl;
	}

	void test1()
	{
		list<int> l;
		l.push_back(1);
		l.push_back(2);
		l.push_back(3);
		l.push_back(4);
		l.push_back(5);
		l.push_back(6);
		l.push_front(0);
		l.push_front(6);
		l.push_front(5);
		l.push_front(4);
		l.push_front(3);
		l.push_front(2);
		l.push_front(1);

		print(l);
	}

	void test2()
	{
		list<int> l;
		l.push_back(1);
		l.push_back(2);
		l.push_back(3);
		l.push_back(4);
		l.push_back(5);
		l.push_back(6);
		print(l);

		l.pop_back();
		print(l);

		l.pop_front();
		print(l);
	}

	struct AA
	{
		int a1 = 1;
		int a2 = 2;
	};

	void test3()
	{
		list<AA> l;
		l.push_back(AA());
		l.push_back(AA());
		l.push_back(AA());
		l.push_back(AA());
		l.push_back(AA());
		l.push_back(AA());

		for (auto it = l.begin(); it != l.end(); ++it)
		{
			cout << it->a1 << " " << it->a2 << endl;
			//cout << it->->a1 << " " << it->->a2 << endl;
			//特殊处理:本来应该是两个->才合理,为了可读性,省略了一个->
			//cout << it.operator->()->a1 << " " << it.operator->()->a2 << endl;
		}
		cout << endl;
	}

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

		auto iti = l.begin();
		l.insert(iti, 10);
		*iti *= 99;
		print(l);

		//删除所有偶数
		auto it = l.begin();
		while (it != l.end())
		{
			if (*it % 2 == 0)
			{
				l.erase(it);
			}
			++it;
		}

		print(l);
	}

	void test5()
	{
		list<int> l1;
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		l1.push_back(5);
		cout << "l1:"; print(l1);

		list<int> l2 = l1;

		cout << "l2:"; print(l2);

		list<int> l3;
		l3.push_back(10);
		l3.push_back(20);
		l3.push_back(30);
		l3.push_back(40);
		l3.push_back(50);
		l3.push_back(60);
		cout << "l3:"; print(l3);

		l1 = l3;

		cout << "l1:"; print(l3);
	}

	void test6()
	{
		list<int> l;
		l.push_back(1);
		l.push_back(2);
		l.push_back(3);
		l.push_back(4);
		l.push_back(5);
		l.push_back(6);
		l.push_back(7);

		print(l);

		cout << l.front() << endl;
		cout << l.back() << endl;
	}
}

int main()
{
	//ybc::test1();

	//ybc::test2();

	//ybc::test3();

	//ybc::test4();

	//ybc::test5();

	ybc::test6();

	return 0;
}

2. list 的反向迭代器(拓展)

通过前面例子知道,反向迭代器的 ++ 就是正向迭代器的 - -,反向迭代器的 - - 就是正向迭代器的 ++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器对正向迭代器的接口进行包装即可。

template<class Iterator>
class ReverseListIterator
{
	// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量
	// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
	// 因为静态成员变量也是按照 类名::静态成员变量名的方式访问的
	public:
	typedef typename Iterator::Ref Ref;
	typedef typename Iterator::Ptr Ptr;
	typedef ReverseListIterator<Iterator> Self;
	public:

	// 构造
	ReverseListIterator(Iterator it): _it(it){}

	// 具有指针类似行为
	Ref operator*(){
		Iterator temp(_it);
		--temp;
		return *temp;
	}
	Ptr operator->(){ return &(operator*());}

	// 迭代器支持移动
		Self& operator++(){
		--_it;
		return *this;
	}
	Self operator++(int){
		Self temp(*this);
		--_it;
		return temp;
	}
	Self& operator--(){
		++_it;
		return *this;
	}
	Self operator--(int)
	{
		Self temp(*this);
		++_it;
		return temp;
	}

	// 迭代器支持比较
	bool operator!=(const Self& l)const{ return _it != l._it;}
	bool operator==(const Self& l)const{ return _it != l._it;}
	Iterator _it;
};

总结

l i s t list list S T L STL STL 封装好的一个双向带头循环链表的模板类。相较于 v e c t o r vector vector 的连续线性空间, l i s t list list 就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,且效率为 O ( 1 ) O(1) O(1)。因此, l i s t list list 对于空间的运用有绝对的精准,一点也不浪费 v e c t o r vector vector l i s t list list 作为两个最常被使用的容器,在上文一个表格中做了对比,两个容器各有优劣根据各自的应用场景使用最合适的容器,就能够发挥出最好的效果。

### 关于C++ STL `list` 的用法 #### 基础概念 标准模板库(STL)中的 `std::list` 是一种双向链表容器,允许高效的插入和删除操作。它不提供随机访问功能,因此索引查找效率较低。 #### 头文件引入 为了使用 `std::list`,需要包含头文件 `<list>`[^4]。 ```cpp #include <list> ``` #### 创建列表 可以通过多种方式创建一个 `std::list` 容器: 1. **默认初始化** ```cpp std::list<int> myList; ``` 2. **指定初始大小** ```cpp std::list<int> myList(5); // 创建一个含有 5 个零值整数的 list ``` 3. **通过迭代器范围初始化** ```cpp std::vector<int> vec = {1, 2, 3}; std::list<int> myList(vec.begin(), vec.end()); // 使用 vector 初始化 list ``` #### 插入与删除 `std::list` 提供了高效的操作用于在任意位置插入或移除元素。 1. **插入元素** - 在头部插入: ```cpp myList.push_front(10); ``` - 在尾部插入: ```cpp myList.push_back(20); ``` - 在特定位置插入: ```cpp auto pos = myList.begin(); ++pos; // 移动到第二个元素的位置 myList.insert(pos, 15); // 在第二个位置前插入 15 ``` 2. **删除元素** - 删除第一个元素: ```cpp myList.pop_front(); ``` - 删除最后一个元素: ```cpp myList.pop_back(); ``` - 删除特定位置的元素: ```cpp auto it = myList.begin(); ++it; // 指向第二个元素 myList.erase(it); // 删除第二个元素 ``` #### 遍历列表 可以使用迭代器遍历整个 `std::list`。 ```cpp for(auto it = myList.begin(); it != myList.end(); ++it){ std::cout << *it << " "; } // 或者使用基于范围的 for 循环 for(const auto& elem : myList){ std::cout << elem << " "; } ``` #### 查找元素 虽然 `std::list` 不支持快速随机访问,但仍可通过算法函数实现查找。 ```cpp auto it = std::find(myList.begin(), myList.end(), 15); if(it != myList.end()){ std::cout << "Element found: " << *it << std::endl; } else { std::cout << "Element not found" << std::endl; } ``` #### 排序与反转 由于 `std::list` 支持就地修改顺序,可以直接调用成员方法完成排序或反转。 1. **排序** ```cpp myList.sort(); // 默认升序排列 ``` 2. **反转** ```cpp myList.reverse(); ``` --- ### 性能特点 - 插入/删除复杂度:O(1),因为只需调整指针。 - 访问复杂度:O(n),需逐一遍历节点找到目标位置。 - 存储开销较高,因每个节点都需要额外存储前后链接信息[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值