基本概念
list
是一个计算机专业术语,在编程语言中List
是类库中的一个类,可以简单视之为双向连结串行,以线性
列的方式管理物件集合。list
的特色是在集合的任何位置增加或删除元素都很快,但是不支持随机存取。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/7d98a1f9bbab265d9d681538e9f8436e.png)
list
常见接口
1.构造函数
函数名称 | 功能简述 |
---|
list() | 构造空的list |
list(size_type n,const value_type& val=value_type()) | 构造的list 中包含n 和值为val 的数据 |
list(const list& x) | 拷贝构造 |
list(Inputiterator first,Inputiterator last) | 用[first,last) (范围:左闭右开) 区间中的元素构造list |
void list_test1()
{
list<int> l1;
list<int> l2(2, 100);
list<int> l3(l2);
int a[] = { 12,34,56,78 };
list<int> l4(a, a + sizeof(a) / sizeof(int));
vector<char> v(3, 'a');
list<char> l5(v.begin(), v.end());
for (auto a : l1)
{
cout << a << " ";
}
cout << endl;
for (auto a : l2)
{
cout << a << " ";
}
cout << endl;
for (auto a : l3)
{
cout << a << " ";
}
cout << endl;
for (auto a : l4)
{
cout << a << " ";
}
cout << endl;
for (auto a : l5)
{
cout << a << " ";
}
cout << endl;
}
输出
100 100
100 100
12 34 56 78
a a a
2.iterator
的使用
函数名称 | 功能简述 |
---|
begin() | 获取第一个数据位置的iterator |
end() | 获取最后一个数据的下一个位置的iterator |
rbegin() | 获取最后一个数据位置的reverse_iterator |
rend() | 获取第一个数据的前一个位置的reverse_iterator |
cbegin()(C++11) | 获取第一个数据位置的const_iterator |
cend()(C++11) | 获取最后一个数据的下一个位置的const_iterator |
crbegin()(C++11) | 即crend() 的位置 |
crend()(C++11) | 即cbegin() 的位置 |
void list_test2()
{
int a[] = { 12,23,34,45,56,67,78,89,90 };
list<int> l1(a, a + sizeof(a) / sizeof(int));
auto it1 = l1.begin();
while (it1 != l1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
auto it2 = l1.rbegin();
while (it2 != l1.rend())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
auto it3 = l1.cbegin();
while (it3 != l1.cend())
{
cout << *it3 << " ";
++it3;
}
cout << endl;
auto it4 = l1.crbegin();
while (it4 != l1.crend())
{
cout << *it4 << " ";
++it4;
}
cout << endl;
}
输出
12 23 34 45 56 67 78 89 90
90 89 78 67 56 45 34 23 12
12 23 34 45 56 67 78 89 90
90 89 78 67 56 45 34 23 12
3.list capacity
的函数
函数名称 | 功能简述 |
---|
bool empty() const | 判断list 是否为空,是返回true ,否返回false |
size_t size() cosnt | 返回list 中有效数据的个数 |
void list_test3()
{
list<int> l1(3, 5);
bool bl = l1.empty();
cout << bl << endl;
size_t sl = l1.size();
cout << sl << endl;
}
输出
0
3
4.元素访问
函数名称 | 功能简述 |
---|
reference front() | 返回list 的第一个节点中值的引用 |
const_reference front() const | 返回list 的第一个节点中值的const 引用 |
reference back() | 返回list 的最后一个节点中值的引用 |
const_reference back() const | 返回list 的最后一个节点中值的const 引用 |
void list_test4()
{
int a[] = { 1,2,4,5,67,8 };
list<int> l1(a, a + sizeof(a) / sizeof(int));
for (auto a : l1)
{
cout << a << " ";
}
cout << endl;
l1.front() = 11;
l1.back() = 88;
for (auto a : l1)
{
cout << a << " ";
}
cout << endl;
const list<int> l2(a, a + sizeof(a) / sizeof(int));
const int& cfl2 = l2.front();
const int& cbl2 = l2.back();
//l2.front() = 11;
//l2.back() = 11;//注释掉的原因,见下图
cout << cfl2 << endl;
cout << cbl2 << endl;
}
输出
1 2 4 5 67 8
11 2 4 5 67 88
1
8
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ef6205224a6e6e404db3b9ea5f15bfb5.png)
5.修改操作
函数名称 | 功能简述 |
---|
void push_front(const value_type& val) | 在list 头插val |
void pop_front() | list 头删 |
void push_back(const_value_type& val) | 在list 尾插val |
void pop_back() | list 尾删 |
template <class… Args> void emplace_front(Args&&…args)(C++11 | 在list 第一个元素前根据参数直接构造元素 |
template <class… Args> void emplace_back(Args&&…args)(C++11 | 在list 最后一个元素后根据参数直接构造元素 |
template <class… Args> iterator emplace (const_iterator position, Args&&… args)(C++11 | 在list 的position 位置根据参数直接构造元素 |
iterator insert (const_iterator position, const value_type& val) | 在list 的position 位置插入val |
void insert(iterator position,size_type n,const value_type& val) | 在list 的position 位置插入n 个val |
template iterator insert (const_iterator position, InputIterator first, InputIterator last) | 在list 的position 位置插入[first,last) (范围:左闭右开) 中的元素 |
iterator erase(iterator position) | 删除list 中position 位置的元素 |
iterator erase(iterator first,iterator last) | 删除list 中[first,last)(范围:左闭右开) 中的元素 |
void swap(list& x) | 交换两个list 中的元素 |
void resize(size_type n,value_type val =value_type()) | 将list 中有效元素改变至n 个,多出的元素用val 填充,在int 中默认val=0 ,如果是自定义类型,就调用缺省构造函数 |
void clear() | 清空list 中的有效元素 |
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{}
static void Print(const Date& d)
{
cout << d._year << "-" << d._month << "-" << d._day << endl;
}
private:
int _year;
int _month;
int _day;
};
void list_test5()
{
//push_back()、push_front()、pop_back()、pop_front()
list<int> l1(3, 5);
l1.push_back(1);
l1.push_front(4);
Print_List(l1);
l1.pop_back();
l1.pop_front();
Print_List(l1);
//emplace_front()、emplace_back()、emplace
list<Date> l2;
Date d;
l2.push_back(d);
l2.emplace_back(2019, 11, 14);
l2.emplace_front(2018, 11, 14);
auto it2 = l2.begin();
while (it2 != l2.end())
{
Date::Print(*it2);
++it2;
}
//insert、erase
int a[] = { 1,2,3,4 };
list<int> l3(a,a+sizeof(a)/sizeof(int));
auto it3 = l3.begin();
cout << *it3 << endl;
auto pos = find(l3.begin(), l3.end(), 2);
l3.insert(pos, 22);
l3.insert(it3, 11);
cout << *it3 << endl;//并没有发生迭代器失效
while (it3 != l3.end())
{
cout << *it3 << " ";
++it3;
}
cout << endl;
Print_List(l3);
int b[] = { 12,23,34,45 };
list<int> l4(2, 3);
Print_List(l4);
l4.insert(l4.begin(), b, b + sizeof(b) / sizeof(int));
Print_List(l4);
l4.erase(l4.begin());
Print_List(l4);
l4.erase(l4.begin(), l4.end());
Print_List(l4);
//swap、resize、clear
list<int> l5(3, 3);
list<int> l6(3, 6);
cout << "l5:";
Print_List(l5);
cout << "l6:";
Print_List(l6);
l5.swap(l6);
cout << "swap()之后" << endl;
cout << "l5:";
Print_List(l5);
cout << "l6:";
Print_List(l6);
l5.resize(10,10);
Print_List(l5);
l6.resize(2, 2);
Print_List(l6);
l6.clear();
Print_List(l6);
}
输出
4 5 5 5 1
5 5 5
2018-11-14
1900-1-1
2019-11-14
1
1
1 22 2 3 4
11 1 22 2 3 4
3 3
12 23 34 45 3 3
23 34 45 3 3
l5:3 3 3
l6:6 6 6
swap()之后
l5:6 6 6
l6:3 3 3
6 6 6 10 10 10 10 10 10 10
3 3
迭代器失效
由于list
底层结构是带头结点的双向循环链表,所以list
的
insert
并不会导致迭代器失效,只有在erase
时才会失效
erase()
函数执行后,iterator
所指向的节点已被删除,
因此iterator
无效,所以无法通过++来找到下一个节点,
所以相比于vector
而言,list
中的迭代器失效比较简单,因为list
只有野指针
(iterator
所指向的位置已经被释放)这一种情况
解决方法:重新获取一下
void list_test6()
{
int a[] = { 1,2,3,4,5,6,7 };
list<int> l1(a, a + sizeof(a) / sizeof(int));
Print_List(l1);
auto it1 = l1.begin();
//迭代器失效场景
/*while (it1 != l1.end())
{
l1.erase(it1);
++it1;
}*/
//解决方法:重新获取一下
while (it1 != l1.end())
{
l1.erase(it1++);
//++it1;
}
Print_List(l1);
list<int> l2(a, a + sizeof(a) / sizeof(int));
Print_List(l2);
auto it2 = l2.begin();
while (it2 != l2.end())
{
if (*it2 % 2 != 0)
{
l2.erase(it2++);
}
else
//++it2;
++it2;
}
Print_List(l2);
}
输出
1 2 3 4 5 6 7
1 2 3 4 5 6 7
2 4 6
vector
与list
的对比
| vector | list |
---|
底层结构 | 动态顺序表,连续的一段空间 | 带头结点的双向链表 |
随机访问 | 支持随机访问,时间复杂度为O(1) | 不支持随机访问,访问任意元素,时间复杂度为O(N) |
插入和删除 | 任意位置插入和删除效率低,需要移动元素,时间复杂度O(N),插入时可能会增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率低下 | 任意位置的插入删除效率高,不需要移动元素,时间复杂度为O(1) |
空间利用率 | 底层是一片连续的空间,不容易造成内存碎片化,空间利用率高,缓存利用率高 | 底层结点动态开辟,小结点容易造成内存碎片化,空间利用率低,缓存利用率低 |
迭代器 | 只是一种简单的指针 | 对简单指针的封装 |
迭代器失效 | 插入,删除时均会导致迭代器失效, | 只有删除时才会导致迭代器失效 |
使用场景 | 需要高效率存储,支持随机访问,较少的插入、删除操作 | 大量的插入、删除,不是很需要随机访问 |