list实际上就是一个带头双向循环链表
文档介绍
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
list的接口比较多,我们只需要掌握一些常用的接口即可。
list的构造函数常用的是无参构造和拷贝构造
构造函数 | 函数说明 |
---|---|
list() | 无参构造,得到空的list |
list(const list& x) | 拷贝构造函数 |
list(size_type n, const value_type& val = value_type()) | 构造n个元素且数值为val的list |
list (InputIterator first, InputIterator last) | 使用迭代器构造list |
//常用无参构造和拷贝构造
void test2()
{
//1.默认构造,list() 得到空list
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
//2.拷贝构造 list(const list& x)
list<int> lt2(lt1);
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
//3.list(size_type n, const value_type& val = value_type())
list<int> lt3(10, 1);
for (auto it : lt3)
{
cout << it << " ";
}
cout << endl;
//4.list (InputIterator first, InputIterator last)
list<int> lt4(lt1.begin(), lt1.end());
for (auto it : lt4)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
test2();
return 0;
}
上述构造方法,基本上和vector是一致的,只需要掌握住常用的两种构造方法即可
list支持正向和反向迭代器
begin+end:正向迭代器iteraotr,分别获取list的首元素地址和最后一个元素的下一个地址
rbegin+rend:反向迭代器reverse_iterator,分别获取list的最后一个元素的地址和首元素地址的前一个位置
void test3()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
//begin+end 正向迭代器
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//rbegin+rend 反向迭代器
list<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
int main()
{
test3();
return 0;
}
注意:无论正向还是反向迭代器,都是++迭代器,且list不支持迭代器+n的操作(物理空间不连续)
函数名 | 函数说明 |
---|---|
push_front | 头插 |
pop_front | 头删 |
push_back | 尾插 |
pop_back | 尾删 |
insert | 在pos位置插入数据val |
erase | 删除pos位置/迭代器区间的数据 |
swap | 交换链表(实际上就是交换两个list的成员变量的地址) |
clear | 清空链表(清空数据) |
empty | 判断list是否为空 |
size | 得到list元素个数 |
front | 得到list首元素 |
back | 得到list最后一个元素 |
resize | 更改size大小 |
熟悉掌握这些函数即可,其他函数可以在文档中查看
push和pop的函数
使用规则很简单,如下所示
void test4()
{
list<int> lt;
//1.push_back 尾插
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//2.pop_back 尾删
lt.pop_back();
lt.pop_back();
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//3.push_front 头插
lt.push_front(10);
lt.push_front(20);
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//4.pop_front() 头删
lt.pop_front();
lt.pop_front();
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
test4();
return 0;
}
insert
list的insert不存在迭代器失效,因为不移动数据,erase之后pos位置的迭代器失效,因为节点没了
总结:在pos位置上插入一个或多个数据val,多个数据表示方式:迭代器区间表示插入的多个数据、n*val
void test5()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
//1.iterator insert (iterator position, const value_type& val);
//返回迭代器
lt.insert(lt.begin(), 10);
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//2.void insert (iterator position, size_type n, const value_type& val);
lt.insert(lt.begin(), 5, 100);
lt.insert(lt.begin(), 10);
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//3. void insert (iterator position, InputIterator first, InputIterator last);
int a[] = { 10,20,30,40 };
lt.insert(lt.begin(), a, a + 3);
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
test5();
return 0;
}
erase
void test6()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
//1.iterator erase (iterator position);
lt.erase(lt.begin());
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//由于不能使用lt.begin()+n的操作随意访问,所以当我们要删除指定位置pos的数据的时候,先要遍历得到pos的位置
//比如想要删除第三个数据
int num = 0;
auto it = lt.begin();
while (it != lt.end())
{
num++;
if (num == 3)
{
lt.erase(it);
break;
}
it++;
}
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//2.iterator erase (iterator first, iterator last);
//同上,所以list删除指定位置数据的时候,麻烦一点,主要是得先遍历一遍得到我们想要得到的迭代器pos地址
//全部删除
lt.erase(lt.begin(), lt.end());
if (lt.empty())
{
cout << "当前list为空" << endl;
}
}
int main()
{
test6();
return 0;
}
总结:erase对于list来讲,在取的pos位置上,需要遍历得到(不支持begin()+n的操作)
其他函数
void test7()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
list<int> lt2(lt1);
lt2.pop_back();
lt2.pop_back();
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
//1.swap 交换链表
//在中template void swap (T& a, T& b);
//在list中void swap (list& x);
lt1.swap(lt2);
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
//swap(lt1,lt2)
//2.clear 清空
if (lt1.empty())
{
cout << "当前list为空" << endl;
}
else
{
cout << "list非空" << endl;
}
lt1.clear();
//3.empty 判空
if (lt1.empty())
{
cout << "当前list为空" << endl;
}
else
{
cout << "list非空" << endl;
}
//4.size 得到当前元素的个数
cout << "lt1的元素个数:" << lt1.size() << endl;
cout << "lt2的元素个数:" << lt2.size() << endl;
//5.front 得到第一个元素
cout << lt2.front() << endl;
//6.back 得到最后一个元素
cout << lt2.back() << endl;
//7.resize 更改size大小
lt2.resize(2);
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
test7();
return 0;
}
还有一些函数是比较好玩的,比如merge、unique、reverse、remove、splice
merge表示合并两个链表,需要将两个链表sort排序之后再使用merge合并,lt1.merge(lt2)将lt2中节点都放在了lt1中,lt2成为空链表
unique表示删除重复项,只保留list中唯一项,需要sort排序list,再使用unique
reverse就是将list反转
remove 删除val,如果list没有val,不做处理,如果有,那就删除
splice 转移,将a链表的节点取下来放在b的链表中,可以a作用与于a,但是要保证节点不要重叠
void test9()
{
//测试merge
list<int> lt1;
lt1.push_back(1);
lt1.push_back(3);
lt1.push_back(2);
lt1.push_back(20);
lt1.push_back(2);
list<int> lt2;
lt2.push_back(1);
lt2.push_back(22);
lt2.push_back(34);
lt2.push_back(124);
lt2.push_back(5);
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
//先排序
lt1.sort();
lt2.sort();
lt1.merge(lt2);
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
cout << "lt2元素个数" <<lt2.size() <<endl;
}
void test10()
{
list<int> lt1;
//测试merge 空串也是可以使用的
list<int> lt2;
lt2.push_back(1);
lt2.push_back(22);
lt2.push_back(34);
lt2.push_back(124);
lt2.push_back(5);
lt2.sort();
lt1.merge(lt2);
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
cout << "lt2元素个数" << lt2.size() << endl;
}
//测试unique
void test11()
{
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(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
cout << "lt:";
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
lt.sort();
lt.unique();
cout << "lt:"; //lt:1 2 3 4 5 1 2 3 4 5
for (auto it : lt)
{
cout << it << " ";
}
cout << endl; //lt:1 2 3 4 5
}
//测试remove和reverse
void test12()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.remove(2);
cout << "lt:";
for (auto it : lt)
{
cout << it << " ";
}
cout << endl; //lt:1 3 4 5
lt.remove(20);
cout << "lt:";
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
//reverse逆序lt
lt.reverse();
cout << "lt:";
for (auto it : lt)
{
cout << it << " ";
}
cout << endl;
}
//splice测试
void test13()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
list<int> lt2(lt1);
lt2.pop_back();
lt2.pop_back();
cout << "lt1:";
for (auto it : lt1)
{
cout << it << " ";
}
cout << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
//创建一个空链表lt3
list<int> lt3;
//使用splice,将lt2的第一个节点放在lt3头部
lt3.splice(lt3.begin(), lt2,lt2.begin());
cout << "splice移动之后" << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
cout << "lt3:";
for (auto it : lt3)
{
cout << it << " ";
}
cout << endl;
//将整个lt2链表放在lt3的头部
lt3.splice(lt3.begin(), lt2);
cout << "第一次splice移动之后" << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
cout << "lt3:";
for (auto it : lt3)
{
cout << it << " ";
}
cout << endl;
//区间移动,将lt3的所有内容,移动到lt2中
lt2.splice(lt2.begin(), lt3, lt3.begin(), lt3.end());
cout << "第二次splice移动之后" << endl;
cout << "lt2:";
for (auto it : lt2)
{
cout << it << " ";
}
cout << endl;
cout << "lt3:";
for (auto it : lt3)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
test12();
return 0;
}
现在对于迭代器简单理解为指针即可,迭代器失效就是其所指向的节点的地址失效了,list中只有该节点被删除了,才会使得该节点迭代器失效
在list中,因为自身数据结构的没有扩容这一事情,所以插入不会导致list迭代器失效,只有在删除的时候才会失效,并且失效的是指向被删除节点的迭代器,其他迭代器不受影响。
迭代器分类
迭代器分为三类:单向、双向、随机
迭代器是连接容器和算法的桥梁,提供统一方式访问容器,可以不用去考虑容器的底层结构,就能访问容器
迭代器 | 介绍 |
---|---|
单向(forward iterator) | 单向迭代器只能++,主要是单链表(forward_list)、哈希 |
双向(Bidirectional Iterator) | 双向迭代器,能++ / - -,主要是list和map、set |
随机(random_access_iterator) | 随机迭代器,能++ / +n / - -等操作,主要是vector、string、deque |
list的优点:
1.支持任意位置插入数据,更加效率
2.按需进行释放空间,不会有扩容问题
list的缺点:
1.不能随机访问,即下标访问
2.cpu高速缓存效率低
高速缓存是一个用于存储临时数据的快速存储器,位于CPU和主内存之间。它具有较高的读取和写入速度,能够提供更快的数据访问。
CPU高速缓存是计算机处理器中的一种临时存储器,用于加快对内存中数据的访问速度。当CPU高速缓存效率低时,意味着CPU无法有效利用缓存和内存之间的数据传输,导致程序的执行速度变慢。这可能是由于代码结构不合理、数据访问模式不佳等原因引起的。为了提高CPU高速缓存效率,可以优化代码的布局、调整数据访问方式、避免冲突等措施。