在上一篇篇博客中,我们了解了与string类和vector类中相通的函数,而在这篇博客中,我们来学习一下那些string类和vector类中没有的、list新引入的内容。
4.list的元素访问
C++ list 是一个双向链表,因此不像 vector 或 string 那样可以通过随机访问来获取元素。list 的元素访问需要通过迭代器逐个遍历。由于其双向链表结构,它没有 operator[] 或 at() 这样常见的随机访问函数。
4.1访问首尾元素
- front(): 返回链表中的第一个元素。
- back(): 返回链表中的最后一个元素。
std::list<int> mylist = {1, 2, 3};
int first = mylist.front(); // first == 1
int last = mylist.back(); // last == 3
4.2通过迭代器访问
由于 list 不支持随机访问,你必须通过迭代器来访问中间元素。你可以使用 begin() 和 end() 来遍历元素:
list<int> mylist = {1, 2, 3, 4, 5};
list<int>::iterator it = mylist.begin()
while (it != mylist.end())
{
cout << *it << " ";
++it;
}
如果要访问特定位置的元素,必须从 begin() 开始通过迭代器逐个移动,而无法直接访问某个位置。
4.3list 与 vector 和 string 的元素访问区别
1. 访问机制
- list:不能随机访问,只能通过迭代器逐步遍历。由于链表结构,`list` 的元素并不连续存储,因此获取中间元素时需要从头或尾遍历,时间复杂度是 O(n)。
- vector 和 `string:这两者都支持随机访问。由于 vector 和 string 是基于连续内存块的实现,元素之间按序排列,因此可以通过下标 operator[] 或 at() 以 O(1) 时间复杂度直接访问任意位置的元素。
//vector
std::vector<int> vec = {1, 2, 3, 4};
int second = vec[1]; // 直接访问第二个元素
//string
std::string str = "hello";
char secondChar = str[1]; // 直接访问第二个字符
2.适用场景
- list:更适合需要频频繁在中间插入或删除元素的场景,虽然遍历性能不如 vector,但插入和删除操作的效率较高,特别是在中间位置操作时。
- vector 和 string:适合频繁访问元素、需要随机访问的场景,且内存连续分配,局部性好。vector 尤其适合需要批量操作(如排序、搜索)且元素数量稳定的场景。
3.总结
list 由于链表结构不支持随机访问,访问元素的方式只能通过迭代器逐步遍历。
vector 和 string 则是连续存储,可以通过下标或 at() 进行 O(1) 时间复杂度的快速访问。
在选择容器时,如果访问频繁且随机,vector 和 string 更合适;如果插入和删除操作多,list 更合适。
5. list的操作函数
5.1splice(拼接)
std::list 提供了一种特殊的操作函数 `splice()`,用于将一个链表的元素移动到另一个链表中,而不涉及拷贝或内存重新分配。`splice()` 直接操作链表节点,因此效率非常高,尤其适合需要在不同链表之间移动元素的场景。
splice() 的三种形式
entire list (1)
void splice (const_iterator position, list& x);
single element (2)
void splice (const_iterator position, list& x, const_iterator i);
element range (3)
void splice (const_iterator position, list& x,
1. 移动整个链表
- 功能:将链表 `x` 的所有元素移动到当前链表的 `position` 位置,`x` 变为空链表。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
auto it = list1.begin();
std::advance(it, 2); // 移动迭代器到 list1 的第三个元素
&