C++ 的标准库提供了多种容器,用于存储和管理不同类型的数据。其中,list 是一种基于双向链表的容器,可以高效地支持插入、删除和移动元素等操作。相比于其他容器,例如 vector 和 deque,list 具有更快的插入和删除速度,但访问元素的效率较低。因此,在需要频繁进行插入和删除操作的场景下,list 是一种非常有用的容器。
list 的基本概念和用法
list 是一种基于双向链表(doubly linked list)实现的容器,它的每个元素都包含了指向前一个元素和后一个元素的指针。这种链式结构可以高效地支持插入、删除和移动元素等操作。与数组不同,list 的元素在内存中不是连续存储的,因此可以动态地增加或减少容器的大小。
list 的基本用法非常简单。我们可以使用标准库中的 list 头文件来引入 list 容器,并使用类模板 list
#include
std::list mylist;
list 对象的创建方式与其他容器类似,可以使用默认构造函数创建一个空的 list 对象。我们可以使用 push_back()、push_front()、insert() 等成员函数向 list 中添加元素,使用 erase()、pop_back()、pop_front() 等成员函数删除元素。例如,下面的示例代码演示了如何创建一个包含 3 个元素的 list 对象,并在其中添加和删除元素:
#include
#include
int main() {
std::list mylist;
mylist.push_back(1);
mylist.push_back(2);
mylist.push_front(3);
mylist.insert(mylist.begin(), 4);
mylist.erase(mylist.begin());
mylist.pop_back();
mylist.pop_front();
for (auto it = mylist.begin(); it != mylist.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在上面的示例代码中,我们先创建了一个空的 list 对象 mylist,然后使用 push_back() 和 push_front() 成员函数向 list 中添加元素。insert() 成员函数可以在指定位置插入一个元素,erase() 成员函数可以删除一个指定位置的元素。pop_back() 和 pop_front() 成员函数分别用于删除 list 的最后一个元素和第一个元素。最后,我们使用迭代器遍历 list 中的所有元素,并输出它们的值。注意,这里使用的是 auto 关键字,可以自动推导出迭代器的类型。
需要注意的是,list 的迭代器不支持随机访问,因此不能使用下标运算符 []。如果需要访问 list 中的某个元素,可以使用迭代器的 ++、--、* 等运算符。例如,下面的示例代码演示了如何通过迭代器访问 list 中的元素:
#include
#include
int main() {
std::list mylist = {1, 2, 3};
auto it = mylist.begin();
std::cout << *it << std::endl; // 输出 1
++it;
std::cout << *it << std::endl; // 输出 2
--it;
std::cout << *it << std::endl; // 输出 1
return 0;
}
在上面的示例代码中,我们先创建了一个包含 3 个元素的 list 对象 mylist,然后使用 begin() 成员函数获取 list 的第一个元素的迭代器,并使用 * 运算符访问该元素的值。使用 ++ 和 -- 运算符可以分别移动迭代器到下一个和上一个元素。
list 的高级用法
- 自定义比较函数排序
list的sort()方法可以对元素进行排序,默认使用<运算符进行比较。如果需要按照自定义的比较函数进行排序,可以传入一个比较函数作为参数,例如:
#include
#include
using namespace std;
bool my_compare(int a, int b) {
return a % 10 < b % 10;
}
int main()
{
list my_list {123, 45, 67, 89, 12};
my_list.sort(my_compare);
for (auto it = my_list.begin(); it != my_list.end(); ++it) {
cout << *it << " ";
}
cout << endl;
return 0;
}
- splice()方法
list的splice()方法可以将一个list的元素插入到另一个list中指定的位置,可以用于合并两个list或将一个list中的元素移动到另一个list中,例如:
#include
#include
using namespace std;
int main()
{
list my_list1 {1, 2, 3};
list my_list2 {4, 5, 6};
auto it = my_list1.begin();
// it 表示某个迭代器,n 为整数。该函数的功能是将 it 迭代器前进或后退 n 个位置。
advance(it, 2);
my_list1.splice(it, my_list2);
for (auto it = my_list1.begin(); it != my_list1.end(); ++it) {
cout << *it << " ";
}
// 输出结果:1 2 4 5 6 3
cout << endl;
return 0;
}
- unique()方法
list的unique()方法可以删除相邻的重复元素,
#include
#include
using namespace std;
int main()
{
list my_list{ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 };
my_list.unique();
for (auto it = my_list.begin(); it != my_list.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 输出结果:1 2 3 4
return 0;
}
- merge()方法
list的merge()方法可以将两个有序的list合并为一个有序的list,
#include
#include
using namespace std;
int main()
{
list my_list1{ 1, 3, 5 };
list my_list2{ 2, 4, 6 };
my_list1.merge(my_list2);
for (auto it = my_list1.begin(); it != my_list1.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 输出结果:1 2 3 4 5 6
return 0;
}
总结
总的来说,list是一个非常实用的STL容器,具有以下优点:
- 可以高效地在任意位置插入和删除元素,而不影响其他元素的位置。
- 支持双向迭代器,可以方便地遍历list的元素。
- 支持多种标准算法,可以方便地对list进行排序、查找和删除等操作。
- 支持高级用法,例如自定义比较函数排序、splice、unique和merge等。
在实际编程中,list适用于以下场景:
- 需要频繁在任意位置插入和删除元素的场景。
- 需要对元素进行排序、查找和删除等操作的场景。
- 需要支持高级用法的场景。
需要注意的是,list的内存开销比较大,因为每个元素都需要额外的指针来维护链表结构。在需要高效使用内存的场景下,可以考虑使用其他容器,例如vector或deque。
综上所述,list是一个非常实用的容器,可以用于各种不同的场景。在选择容器时,需要根据实际需求进行选择,合理使用不同的容器可以提高代码的效率和可读性。