list是顺序容器的一种。list是一个双向链表。使用list需要包含头文件list。双向链表的每一个元素中都有一个指针指向后一个元素,也有一个指针指向前一个元素
在list容器中,在已经定位到要增删元素的位置的情况下,增删元素能在常数时间内完成,比如在ai和ai+1之间插入一个元素,值需要修改ai和ai+1中的指针即可
list的内部结构和vector不同,存在明显的区别
1.list不支持随机存取
2.在list的任何位置执行元素的插入和移除都非常快,可以迅速实现。插入和删除动作不会影响指向其他元素的指针、引用、迭代器,不会造成失效
3.list不支持随机存取,不提供下标操作符和at()函数
4.list没有提供容量、空间重新分配等操作函数,每个元素都有自己的内存
5.list提供了特殊成员函数,专门用于移动元素
list模板类实现了标准C++11数据结构中链表的所有功能。list一旦定义了类对象后,就可以完成链表操作
STL 中的算法 sort 可以用来对 vector 和 deque 排序,它需要随机访问迭代器的支持。因为 list 不支持随机访问迭代器,所以不能用算法 sort 对 list 容器排序。因此,list 容器引入了 sort 成员函数以完成排序
list的使用
#include <iostream>
#include <stdlib.h>
#include <list>
using namespace std;
int main()
{
list<int> l1; //构造空的l1
list<int> l2 (4, 100); //l2中放入4个值为100的元素
list<int> l3(l2.begin(), l2.end()); //用l2的begin(),end()左闭右开区间构造l3
list<int> l4(l3); //用l3拷贝构造l4
//以数组作为迭代器区间构造l5
int arr[] = {32,543,23,53};
list<int> l5(arr, arr + sizeof(arr) / sizeof(int));
cout << "l4: data";
//C++11打印方式
for (auto &x : l4)
{
cout << x << " ";
}
cout << endl;
cout << "l5:data:";
//用迭代器方式打印l5中的元素
for (list<int>::iterator it = l5.begin(); it != l5.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
cout << endl;
system("pause");
return 0;
}
list iterator的使用
1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
3. cbegin与cend为const的正向迭代器,与begin和end不同的是:该迭代器指向节点中的元素值不能修 改
4. crbegin与crend为const的反向得带器,与rbegin和rend不同的是:该迭代器指向节点中的元素值不能修改
#include <iostream>
#include <stdlib.h>
#include <list>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> v(a, a + sizeof(a) / sizeof(int));
//使用正向迭代器打印list中的元素
for (list<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//使用反向迭代器打印list中的元素
for (list<int>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it)
{
cout << *it << " ";
}
cout << endl;
for (auto cit : v)
{
cout << cit << " ";
}
system("pause");
return 0;
}
list capacity
#include <iostream>
#include <stdlib.h>
#include <list>
using namespace std;
int main()
{
int a[] = {1,2,3,4,5,6,7,8,9,0};
list<int> v(a, a + sizeof(a) / sizeof(a[0]));
//打印list中有效节点的个数
cout << v.size() << endl;
//检测list是否为空
if (v.empty()){
cout << "空的list" << endl;
}
else
{
for (const auto&e : v)
{
cout << e << " ";
}
cout << endl;
}
system("pause");
return 0;
}
#include <iostream>
#include <stdlib.h>
#include <list>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> v(a, a + sizeof(a) / sizeof(a[0]));
for (auto&e : v)
{
cout << e << " ";
}
cout << endl;
//将list中的第一个节点与最后一个节点中的值改为10
v.front() = 10;
v.back() = 10;
for (auto&e : v)
{
cout << e << " ";
}
const list<int> v2(a, a + sizeof(a) / sizeof(a[0]));
const int& ca = v2.front();
cout << endl;
cout << ca << " ";
system("pause");
return 0;
}
// push_back/pop_back/push_front/pop_front
//打印函数
void PrintList(list<int>&v)
{
for (auto&e : v)
{
cout << e << " ";
}
cout << endl;
}
//////////////////////////////////////////////////////
// push_back/pop_back/push_front/pop_front
void TestList1()
{
int a[] = { 1, 2, 3 };
list<int> v(a, a + sizeof(a) / sizeof(a[0]));
//在list的尾部插入4,头部插入0
v.push_back(4);
v.push_front(0);
PrintList(v);
//删除尾节点和头结点
v.pop_back();
v.pop_front();
PrintList(v);
}
// emplace_back / emplace_front / emplace
// emplace_back / emplace_front / emplace
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date&d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date(const Date&):" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
// push_back尾插:先构造好元素,然后将元素拷贝到节点中,插入时先调构造函数,再调拷贝构造函数
// emplace_back尾插:先构造节点,然后调用构造函数在节点中直接构造对象
// emplace_back比push_back更高效,少了一次拷贝构造函数的调用
void TestList2()
{
list<Date> l;
Date d(2018, 10, 20);
l.push_back(d);
l.emplace_back(2018, 10, 21);
l.emplace_front(2018, 10, 19);
}
// insert /erase
// insert /erase
void TestList3()
{
int a[] = { 1, 2, 3 };
list<int> v1(a, a + sizeof(a) / sizeof(a[0]));
//获取链表中第二个节点
auto pos = ++v1.begin();
cout << *pos << endl;
//在pos前插入值为4的元素
v1.insert(pos, 4);
PrintList(v1);
//在pos前插入5个值为5的元素
v1.insert(pos, 5, 5);
PrintList(v1);
//在pos前插入[v.begin(),v.end())区间中的元素
vector<int> v2{ 7, 8, 9 };
v1.insert(pos, v2.begin(), v2.end());
PrintList(v1);
//删除pos位置上的元素
v1.erase(pos);
PrintList(v1);
//删除list中[begin,end)区间中的元素,即删除所有元素
v1.erase(v1.begin(), v1.end());
PrintList(v1);
}
// resize/swap/clear
void TestList4()
{
int a[] = { 1, 2, 3 };
list<int> v1(a, a + sizeof(a) / sizeof(a[0]));
PrintList(v1);
//将v中的元素个数增加到10个,多出的元素用默认值填充
//list中放置的是内置类型,默认值为0,如果list中放置自定义类型元素,调用缺省构造函数
v1.resize(10);
PrintList(v1);
//将v中的元素增加到20个,多出的元素用4来填充
v1.resize(20, 4);
PrintList(v1);
//将V中的元素减少到5个
v1.resize(5);
PrintList(v1);
//用Vector中的元素来构造list
vector<int> v2{ 4, 5, 6 };
list<int> l(v2.begin(), v2.end());
cout << "构造后l:";
PrintList(l);
//交换v1和l中的元素
v1.swap(l);
cout << "v1:";
PrintList(v1);
cout << "l:";
PrintList(l);
//将l中的元素清空
cout << "清空后:";
l.clear();
cout << l.size() << endl;
}
list的迭代器失效
迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代 器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响
void TestListIterator1()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋
值
l.erase(it);
++it;
}
}
// 改正
void TestListIterator()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
l.erase(it++); // it = l.erase(it);
}
}