目录

1. 什么是 list

2. list 的特点

list 的优点

list 的缺点

3. list 的常见构造函数

(1)构造空 list

(2)构造 n 个 val

(3)拷贝构造

(4)迭代器区间构造

4. list 的迭代器

正向迭代器

5. 迭代器的类型

(1)输入迭代器 Input Iterator

(2)输出迭代器 Output Iterator

应用

(3)单向迭代器 Forward Iterator

典型容器

(4)双向迭代器 Bidirectional Iterator

(5)随机访问迭代器 Random Access Iterator

6. 五种迭代器关系

6. list 的容量相关接口

empty()

size()

7. list 的元素访问

front()

back()

8. list 的修改操作

push_back()

push_front()

pop_back()

pop_front()

insert()

erase()

clear()

splice()

9. list的排序


1. 什么是 list

在 C++ STL 中,list 是一个非常重要的序列式容器,它的底层结构是 带头结点的双向循环链表

vector 不同,list 中的元素在内存中不是连续存储的,而是通过节点之间的指针连接起来,因此它更擅长进行频繁的插入和删除操作。


2. list 的特点

list 的优点

  • 任意位置插入、删除效率高

  • 插入删除不会大量搬移数据

  • 不会像 vector 一样频繁扩容

  • 插入时不会导致其它迭代器失效

list 的缺点

  • 不支持随机访问

  • 访问元素效率低

  • 节点额外存储指针,空间利用率低

  • 缓存命中率较低


3. list 的常见构造函数

(1)构造空 list

list<int> l1;

创建一个空链表。


(2)构造 n 个 val

list<int> l2(5, 10);

创建 5 个值为 10 的节点。

结果:

10 10 10 10 10

(3)拷贝构造

list<int> l3(l2);

使用已有 list 构造新 list。


(4)迭代器区间构造

int array[] = {1,2,3,4,5};

list<int> l4(array, array + 5);

使用区间 [first, last) 中的数据构造 list。


4. list 的迭代器

由于 list 底层是链表,因此不能使用下标访问。

STL 使用 迭代器 iterator 来访问元素。


正向迭代器

begin()
end()
  • begin() 指向第一个元素

  • end() 指向最后一个元素的下一个位置

示例:

list<int> l = {1,2,3,4,5};

list<int>::iterator it = l.begin();

while(it != l.end())
{
    cout << *it << " ";
    ++it;
}

输出:

1 2 3 4 5

5. 迭代器的类型

STL 根据迭代器能力的不同,将迭代器分为五类


(1)输入迭代器 Input Iterator

特点

  • 只能读
  • 只能单向移动
  • 不能修改数据

支持操作

++
*
==
!=

应用场景

用于:

读取数据

例如:

  • 输入流
  • 文件读取

(2)输出迭代器 Output Iterator

特点

  • 只能写
  • 只能向前移动

示例

ostream_iterator<int> out(cout, " ");

应用

copy(v.begin(), v.end(), out);

(3)单向迭代器 Forward Iterator

特点

  • 可读可写
  • 只能向前移动

支持

++
*
->

典型容器

forward_list

单向链表。


(4)双向迭代器 Bidirectional Iterator

特点

在前向迭代器基础上:

新增:

--

支持向前和向后移动。


典型容器

list
set
map
multimap

示例

list<int> lt = {1,2,3};

auto it = lt.end();

--it;

cout << *it;

(5)随机访问迭代器 Random Access Iterator

功能最强

支持:

+
-
[]
<
>
<=
>=

可以像数组一样随机访问。


典型容器

vector
deque
string

示例

vector<int> v = {1,2,3,4,5};

cout << v.begin()[2];

输出:

3

6. 五种迭代器关系

能力是逐渐增强的。

输入/输出迭代器
        ↓
前向迭代器
        ↓
双向迭代器
        ↓
随机访问迭代器

6. list 的容量相关接口

empty()

判断是否为空。

if(l.empty())
{
    cout << "为空";
}

size()

获取有效节点个数。

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

7. list 的元素访问

front()

获取头元素。

cout << l.front();

back()

获取尾元素。

cout << l.back();

8. list 的修改操作

push_back()

尾插。

l.push_back(10);

push_front()

头插。

l.push_front(20);

pop_back()

尾删。

l.pop_back();

pop_front()

头删。

l.pop_front();

insert()

任意位置插入。

这里list迭代器是双向迭代器,不支持+ - 操作,因此不能像vector一样来直接begin+数字来访问迭代器

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);
	lt.push_back(6);

	auto it = lt.begin();
	int k = 3;
	while (k--)
	{
		++it;
	}

	lt.insert(it, 30);
}

第三个位置插入


erase()

删除指定位置。

跟插入操作访问迭代器是一样的

l.erase(pos);

clear()

清空所有元素。

l.clear();

splice()

转移链表节点

剪切链表的一段数据到另一个链表的某个位置

void test_list()
{
	// 一个链表节点转移给另一个链表
	std::list<int> mylist1, mylist2;
	std::list<int>::iterator it;

	// set some initial values:
	for (int i = 1; i <= 4; ++i)
		mylist1.push_back(i);      // mylist1: 1 2 3 4

	for (int i = 1; i <= 3; ++i)
		mylist2.push_back(i * 10);   // mylist2: 10 20 30

	it = mylist1.begin();
	++it;                         // points to 2

	mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
	// mylist2 (empty)
	// "it" still points to 2 (the 5th element

}

/调整当前链表节点的顺序

// 调整当前链表节点的顺序
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	int x = 0;
	cin >> x;
	it = find(lt.begin(), lt.end(), x);
	if (it != lt.end())
	{
		//lt.splice(lt.begin(), lt, it);
		lt.splice(lt.begin(), lt, it, lt.end());
	}

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

假设输入3

输出

1 2 3 4 5 6
3 4 5 6 1 2

9. list的排序

algorithm算法库有一个排序接口,底层用的快排逻辑,这样就涉及到了+ -操作,像vector这样的容器是随机迭代器,我们可以直接使用

sort(vector.begin(),vector.end());

但是像list这样的双向迭代器,我们就没法用算法库的排序接口,但是list类里面自己实现了排序接口

list<int> lt;
lt.sort()

排序默认是升序,如果我们要进行降序,就要传一个仿函数

lt.sort(greater<int>());

更多推荐