全面理解:C++中list(双向链表)容器的基础概念与函数解析
list容器的基础概念
在 C++ 标准库中,list 是一种双向链表,允许在任何位置进行快速的插入和删除操作。
以下是一些关于 list 的基本信息。
-
存储:list 是一种双向链表结构,它不像 vector 或 array 那样提供连续的内存存储。
-
时间复杂度:
list 在链表的开始、结束或中间位置插入和删除元素的时间复杂度是 O(1)。然而,查找元素的时间复杂度是 O(n)。 -
空间复杂度:由于链表中每个元素都需要额外存储其前后节点的信息,因此 list 相比于使用连续内存的容器(如 vector 或 array)具有更高的空间复杂度。
-
迭代器失效:
当你插入或删除元素时,list 的所有迭代器,包括指向链表开始和结束位置的迭代器,都不会失效。唯一的例外是当你删除一个元素时,指向该元素的迭代器将失效。 -
元素访问:
list 不支持随机访问。要访问 list 中的元素,你必须从链表的开始(或结束)位置遍历到你要访问的位置。因此,list 不提供 operator[] 函数(下标运算符)。
list的函数解析
- list有各种各样的成员函数,下面我将以函数的功能对这些函数分一下类,然后按照类别,一一介绍这些函数的用处。
构造函数
list():创建一个空的 listlist(n, elem): 创建一个包含 n 个元素的 list,每个元素的值都是 elemlist(begin, end): 创建一个包含 [begin, end) 区间元素的 list
以下是使用这些构造函数创建 std::list 的示例:
#include <iostream>
#include <list>
#include <vector>
int main() {
// 创建一个空的 list
std::list<int> list1;
// 创建一个包含 5 个元素的 list,每个元素的值都是 10
std::list<int> list2(5, 10);
// 从 vector 创建 list
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> list3(vec.begin(), vec.end());
// 打印 list2
for(int i : list2) {
std::cout << i << " ";
}
std::cout << "\n";
// 打印 list3
for(int i : list3) {
std::cout << i << " ";
}
return 0;
}
元素访问函数
- front(): 返回首元素
- back(): 返回尾元素
注意:这里是返回的是链表的元素,而不是指向元素的迭代器或指针。
各种迭代器函数
- begin(): 返回指向首元素的迭代器
- end(): 返回指向尾元素的下一个位置的迭代器
- rbegin(): 返回指向尾元素的反向迭代器
- rend(): 返回指向首元素的前一个位置的反向迭代器
在这里我想详细讲解一下这个迭代器:
你可以把list的这个这个迭代器,详细成一个高级的指针。- 迭代器是一个对象(指针),假如说list里面的节点元素都是一个个的值了话,这个迭代器就是一个指向这个值的一个指针罢了。我们就可以用解引用符 * 去修改这个值。
- 假如说迭代器指向的这个对象它是一个类,或者是一个结构体,它有成员变量,那你就可以用箭头符 -> 去操作这个对象的成员变量。
你可以把这个对象想象成你自己定义的Listnode,你用箭头符去操作它,hh
判断容器容量函数:
empty():判断容器是否为空size():返回容器中元素的个数max_size():返回容器能够容纳的最大元素数量
添加操作
insert(position, elem): 在 position 位置前插入一个元素push_back(elem): 在 list 的尾部添加一个元素push_front(elem): 在 list 的头部添加一个元素
删除操作
clear(): 删除所有元素erase(position): 删除 position 位置的元素pop_back(): 删除 list 尾部的一个元素pop_front(): 删除 list 头部的一个元素unique(): 删除所有相邻重复元素remove_if(predicate): 删除满足条件 predicate 的所有元素remove(elem): 删除所有值为 elem 的元素
修改容器大小的函数
resize(size, elem): 改变容器的大小,如果需要添加元素,则元素值为 elem
具体讲解一下splice()这个函数
splice() 是list 的一个非常有用的成员函数,它可以高效地将元素从一个列表移到另一个列表,或者从列表的一个位置移动到另一个位置。因为它是重新链接节点指针,不会导致元素内容的复制或移动,所以速度非常快。它一共有三种重载版本。
三种重载版本如下:
-
void splice (const_iterator position, list& x);- 将 x 中的所有元素移到当前列表的 position 位置。 x 变为空。
-
void splice (const_iterator position, list& x, const_iterator i);- 将 x 中的元素 i 移到当前列表的 position 位置。 i 在 x 中的元素被移除。
-
void splice (const_iterator position, list& x, const_iterator first, const_iterator last);-
将 x 中的 [first, last) 区间的元素移到当前列表的 position 位置。 这个区间在 x 中的元素被移除。
-
注意:position 和 x 可以指向同一个列表,但 position 不能在 [first, last) 区间内。
-
下面是使用 splice() 的例子:
#include <iostream>
#include <list>
int main() {
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
// move all elements of list2 to list1
list1.splice(list1.end(), list2);
std::cout << "list1: ";
for (int num : list1) {
std::cout << num << " ";
}
std::cout << "\n";
std::cout << "list2: ";
for (int num : list2) {
std::cout << num << " ";
}
std::cout << "\n";
// move back one element from list1 to list2
list2.splice(list2.begin(), list1, --list1.end());
std::cout << "list1: ";
for (int num : list1) {
std::cout << num << " ";
}
std::cout << "\n";
std::cout << "list2: ";
for (int num : list2) {
std::cout << num << " ";
}
std::cout << "\n";
return 0;
}
在这个例子中,我们首先创建了两个列表。然后,我们将 list2 中的所有元素移动到 list1 的尾部。接着,我们将 list1 的最后一个元素移动到 list2 的头部。每次操作后,我们都打印出两个列表的内容。
交换元素的函数
- swap(list): 交换两个 list
反转链表函数
- reverse(): 反转 list
对链表进行排序的函数
- sort(): 排序 list
这是一些使用 list 的示例:
#include <iostream>
#include <list>
int main() {
std::list<int> numbers;
// push elements
numbers.push_back(5);
numbers.push_back(10);
numbers.push_front(1);
// iterate through the list
for(std::list<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << ' ';
}
std::cout << '\n';
// remove an element
numbers.remove(1);
for(auto &num : numbers) {
std::cout << num << ' ';
}
std::cout << '\n';
// sort the list
numbers.sort();
for(auto &num : numbers) {
std::cout << num << ' ';
}
std::cout << '\n';
return 0;
}
这段代码首先创建了一个空的 list,然后在尾部和头部添加了一些元素。然后它通过迭代器遍历 list 并打印每个元素。然后它删除一个元素,并再次打印 list。最后,它对 list 进行排序,并打印排序后的 list。
总结
-
在C++标准库中,list是一种双向链表,允许在任何位置进行快速的插入和删除操作。list在任何位置插入和删除元素都有相对稳定的性能,而不是依赖于元素在容器中的位置。
-
我们讨论了list的多种构造函数,包括默认构造函数、初始化列表构造函数、以及从其他容器复制元素的构造函数。
-
我们也介绍了 splice() 函数,这是一个非常有用的成员函数,它可以高效地将元素从一个列表移到另一个列表。splice()有三种重载版本,它们都不会导致元素内容的复制或移动,而是重新链接节点指针,因此速度非常快。
最后,我们讨论了一些其他的std::list成员函数,包括元素访问、迭代器、容量查询、列表修改和操作等。
最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容。