前言
list是重要的容器了解它的常见接口以及使用是很有必要的,为什么有了vector还要有list呢?因为vector存在一些缺陷,比如:容量满了要扩容,扩容是要付出代价的(性能的损失),存在空间的浪费,而且vector的头删头插或者中间位置的插入删除都需要挪动数据(时间复杂度O(n))因此比较慢。而list的存在就是为了弥补vector的缺点,list不需要扩容,不存在空间的浪费,任意位置的插入和删除都很快(时间复杂度为(O1))。下面让我们一起来了解一下list的使用吧!
目录
1.构造函数
2.迭代器
3.插入删除
4.容量相关
4.1容量相关
4.2取出表头表尾数据
5.list的迭代器失效
1.构造函数
例如:
void testList1()
{
list<int>l1;//构造空的对象
list<int>l2(5, 2);//构造5个val值的对象
list<int>l3(l2);//拷贝构造
//尾删尾插
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
l1.pop_back();
//迭代器正向遍历
list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//范围for
for (auto e : l2)
{
cout << e << " ";
}
}
2.迭代器
void testList1()
{
list<int>l1;//构造空的对象
list<int>l2(5, 2);//构造5个val值的对象
list<int>l3(l2);//拷贝构造
//尾删尾插
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
l1.pop_back();
//迭代器正向遍历
list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//范围for
for (auto e : l2)
{
cout << e << " ";
}
}
void print(const list<int>& l1);
void testList2()
{
list<int> l1;
//头插头删
l1.push_front(0);
l1.push_front(-1);
l1.push_front(-2);
l1.push_front(-3);
l1.push_front(-4);
l1.push_front(-5);
l1.pop_front();
l1.pop_front();
//迭代器逆向循环
list<int>::reverse_iterator it = l1.rbegin();
while (it != l1.rend())
{
cout << *it << " ";
it++;
}
cout << endl;
print(l1);
}
void print(const list<int>& l1)
{
//const类型的迭代器
list<int>::const_iterator it = l1.cbegin();
while (it != l1.cend())
{
//*it += 1;
cout << *it << " ";
it++;
}
}
注意const类型的对象遍历需要const类型的迭代器,如上。
范围for是靠迭代器支持的,所以支持迭代器就支持范围for(c++11语法)。
begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
rbegin和rend为反向迭代器。对迭代器执行++操作,迭代器向前移动
3.插入删除
push_back | 在list的尾部插入一个值为val的元素 |
pop_back | 删除list尾部的元素 |
push_front | 在list的第一个元素之前插入值为val的元素 |
pop_back | 删除list的第一个元素 |
insert | 任意位置的插入 |
erase | 任意合法位置的删除 |
swap | 交换两个list中的元素 |
clear | 清空list中的有效元素 |
例如:
void testList2()
{
list<int> l1;
//头插头删
//尾删尾插
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
l1.pop_back();
l1.push_front(0);
l1.push_front(-1);
l1.push_front(-2);
l1.push_front(-3);
l1.push_front(-4);
l1.push_front(-5);
l1.pop_front();
l1.pop_front();
//迭代器逆向循环
list<int>::reverse_iterator it = l1.rbegin();
while (it != l1.rend())
{
cout << *it << " ";
it++;
}
cout << endl;
}
void testList3()
{
//
list<int> l1;
l1.insert(l1.begin(), 1);//调用insert头插
l1.insert(l1.begin(), 2);
l1.insert(l1.begin(), 3);
l1.insert(l1.begin(), 4);
l1.insert(l1.begin(), 5);
l1.insert(l1.begin(), 6);
for (auto& e : l1)
{
cout << e << "->";
}
cout << endl;
l1.insert(l1.end(), 10);//用insert尾插
l1.insert(l1.end(), 20);
l1.insert(l1.end(), 30);
l1.insert(l1.end(), 40);
l1.erase(--l1.end());//调用erase尾删
for (auto& e : l1)
{
cout << e << "->";
}
cout << endl;
list<int>l2(6, -2);//构造6个2
cout << "l2交换前:" << endl;
for (auto& e : l2)
{
cout << e << "->";
}
cout << endl;
swap(l1, l2);//交换l1和l2
cout << "l2交换后:" << endl;
for (auto& e : l2)
{
cout << e << "->";
}
cout << endl;
l2.clear();//清空l2
for (auto& e : l2)
{
cout << e << "->";
}
cout << endl;
}
注意list是不支持随机访问的,所以在调用erase和insert,迭代器不能加一个常数传给erase和insert ,如:
list<int> l1;
l1.insert(l1.begin(), 1);//调用insert头插
l1.insert(l1.begin(), 2);
l1.insert(l1.begin(), 3);
l1.insert(l1.begin(), 4);
l1.insert(l1.begin(), 5);
l1.insert(l1.begin(), 6);
//下面这两个是错误的用法
l1.insert(l1.end()+5, 40);
l1.erase(l1.end()-3);//调用erase尾删
insert的函数原型:
使用它要给它一个迭代器,不管是插入一个val还是n个val。
erase的函数原型:
给它的如果是一个迭代器删除的就是这个迭代器位置的val,如果是一段迭代器区间删除的就是这段迭代器区间的val。注意不能传end() 给它,程序会奔溃,因为end()迭代器实际上是头结点,在list对象生命周期没有结束的时候头结点是不可以被删除的,不然如果后面再去操作这个链表就会有内存访问冲突的问题。
4.容量相关
4.1容量相关
函数说明 | 接口说明 |
empty | 判断链表是否为空如果为空返回true,反之返回false |
size | 返回list中有效节点的个数 |
void testList4()
{
//
list<int> l1;
l1.insert(l1.begin(), 1);//调用insert头插
l1.insert(l1.begin(), 2);
l1.insert(l1.begin(), 3);
l1.insert(l1.begin(), 4);
l1.insert(l1.begin(), 5);
l1.insert(l1.begin(), 6);
for (auto& e : l1)
{
cout << e << "->";
}
cout << endl;
cout << l1.size() << endl;
while (!l1.empty())//删除链表表头元素的方式遍历链表元素
{
cout << l1.front() << "->";//取出表头元素
l1.pop_front();//删除头节点
}
cout << endl;
}
4.2取出表头表尾数据
函数声明 | 接口说明 |
front | 返回list第一个节点中值的引用 |
back | 返回list最后一个节点中值的引用 |
例如:
void testList5()
{
//
list<int> l1;
l1.insert(l1.begin(), 1);//调用insert头插
l1.insert(l1.begin(), 2);
l1.insert(l1.begin(), 3);
l1.insert(l1.begin(), 4);
l1.insert(l1.begin(), 5);
l1.insert(l1.begin(), 6);
cout << l1.front() << endl;//输出链表第一个节点的值
cout << l1.back() << endl;//输出链表最后一个节点的值
}
5.list的迭代器失效
当我们在迭代器的某个位置插入一个元素迭代器会失效吗?请和我一起看看下面的代码!
void testList5()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
l1.push_back(6);
l1.push_back(7);
list<int>::iterator it = l1.begin();
it++;//到下一个节点的迭代器
l1.insert(it, -4);//在这个迭代器这里插入一个val值迭代器会失效
l1.insert(it, -5);//在这里继续插入一个val值程序会奔溃吗?
}
实际上在list中插入元素迭代器是不会失效的。这与list的物理结构有关,因为插入一个节点,原来的节点是没有删除的,只是申请了一个新的节点然后与原来的节点建立联系,插入数据。
如果删除一个元素呢?我们来试试
void testList7()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
l1.push_back(6);
l1.push_back(7);
list<int>::iterator it = l1.begin();
l1.erase(it);//删除节点的元素
l1.insert(it,-4);
}
如果删除了这个节点,那么再对这个节点的迭代器进行操作程序就会报错,实际上这块内存已经被delete了,所以已经返还给操作系统了再进行其他操作就是不被允许的了。