文章目录
# 0 list
list是一个双向带头循环链表。
要使用list,需包含头文件:
#include<list>
list底层的空间并不连续,所以没有了[ ]的重载,不支持使用下标访问。
以及没有扩容这种概念,因为链表的空间以节点为单位,按需申请释放。
# 1 迭代器
list的迭代器不再是原生指针,因为底层的空间并不连续。
从实现的角度上说,list的迭代器与vector大不相同。
但是从使用的角度上来看,迭代器的意义便是给所有容器提供统一的访问与遍历方式。
所以list和其他容器的迭代器,在使用上完全相同。
依旧是这八个函数,依旧是原本的使用方法。
不再赘述了。
# 2 默认成员函数
对大部分容器来说,析构函数会自动调用,两个取地址重载也不需要自己调用。
所以需要掌握使用的只有构造,拷贝构造和赋值重载。
其中拷贝构造和赋值重载的使用极其简单。
也就构造有多个重载,需要列出来看看。
三种构造,分别是:
1.无参
2.创建有n个节点的链表,每个节点的值为val。
val有缺省值。
3.用一段迭代器区间构造
第四个是拷贝构造,用另一个list来初始化。
void Test1()
{
list<int> l1;
for (auto e : l1)
{
cout << e << " ";
}
cout << endl;
list<int> l2(10, 5);
for (auto e : l2)
{
cout << e << " ";
}
cout << endl;
list<int> l3(l2.begin(), l2.end());
for (auto e : l3)
{
cout << e << " ";
}
cout << endl;
list<int> l4 = l3;
for (auto e : l4)
{
cout << e << " ";
}
cout << endl;
}
# 3 容量相关函数
由于没有容量的概念。list并没有reserve函数。
但是依然有resize函数。
# 3.1 empty
判空,空则返回1,非空则返回0。
没什么好说的。
void Test2()
{
list<int> l1;
for (auto e : l1)
{
cout << e << " ";
}
cout << endl;
cout << l1.empty() << endl;
list<int> l2(5, 1);
for (auto e : l2)
{
cout << e << " ";
}
cout << endl;
cout << l2.empty() << endl;
}
# 3.2 size
返回list中的节点个数。
同时也是数据个数。
# 3.3 resize
重新指定list的节点个数。
有这么三种情况:
1.目标size与原size相同:
那当然是什么都不做。
2.目标size大于原size:
将会不断地尾插新节点,直到总节点数量达到指定size。
这些节点的值就是val,可以缺省,调用数据类型的默认构造。
3.目标size小于原size:
那么数据将会被截断。
保留原链表的前目标size个数据,后续的数据将被删除,相应的空间也会被释放。
void Test3()
{
// 原size是6
list<int> l(6, 7);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// resize到6,没有变化
l.resize(6);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// resize到10,使用缺省值,对于整型来说就是0
l.resize(10);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// reszie到1,后续数据都被删除
l.resize(1);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// resize到5,用10来填充
l.resize(5, 10);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
}
# 4 内部数据访问与修改
list有push_front,pop_front,push_back,pop_back这些函数,即头插头删,尾插尾删。
这些函数操作,本质上都是通过insert和erase,
也就是将目标迭代器位置指定成头,或者尾来实现的。
# 4.1 任意位置插入 insert
三种重载:
1.单个数据的插入。
参数为目标位置的迭代器和插入的值。
2.多个数据的填充。
参数为目标位置的迭代器,要插入填充的节点数量n,和填充的值val。
3.多个数据的插入。
参数为目标位置的迭代器,以及一段迭代器区间。
void Test4()
{
list<int> l(3, 3);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// 插入单个数据
l.insert(l.end(), 4);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// 插入2个5
l.insert(l.end(), 2, 5);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// 将ll尾插到l
list<int> ll(5, 1);
l.insert(l.end(), ll.begin(), ll.end());
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
}
# 4.2 任意位置删除 erase
两种重载:
1.单个数据删除。
删除目标迭代器位置的节点。
2.多个数据删除。
删除一段迭代器区间内的所有节点。
void Test5()
{
list<int> l;
l.insert(l.end(), 1);
l.insert(l.end(), 2);
l.insert(l.end(), 3);
l.insert(l.end(), 4);
l.insert(l.end(), 5);
l.insert(l.end(), 6);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// 删除单个数据
l.erase(l.begin());
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
// 删除一段迭代器区间
l.erase(l.begin(), l.end());
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
}
# 4.3 交换 swap
交换两个list。
# 4.4 清空 clear
清空list内的所有数据。
并不是删除整个链表,只是删除整个链表中的数据,头节点还保留着。
还支持继续插入新数据。
# 5 其他操作
# 5.1 排序 sort
list内自带的sort本质上是快排,效率并不高。
# 5.2 去重 unique
可以去除链表中连续的重复值。
如果重复值并不连续,unique函数并不能正确的识别他们。
可以搭配前面的sort使用,达到真正的去重。
void Test6()
{
list<int> l;
l.insert(l.end(), 1);
l.insert(l.end(), 2);
l.insert(l.end(), 3);
l.insert(l.end(), 4);
l.insert(l.end(), 5);
l.insert(l.end(), 2);
l.insert(l.end(), 2);
l.insert(l.end(), 2);
l.insert(l.end(), 6);
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
l.unique();
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
l.sort();
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
l.unique();
for (auto e : l)
{
cout << e << " ";
}
cout << endl;
}
# 5.3 转移 splice
将一个链表的节点,插入到另一个链表中的目标节点后。
两个链表可以是同一个链表,即支持自己对自己的转移操作。
转移操作并不是拷贝,被转移的节点是在两个,或者一个链表中移动。
三种重载:
1.整个链表的转移。
将链表x的所有节点转移到目标迭代器位置。
2.单个节点的转移。
同样是将链表x转移到目标迭代器位置,
不同的是并非整个链表,转移的仅是链表x的 i 迭代器位置所对应的节点。
3.一段迭代器区间的转移。
将链表x的一段迭代器区间转移到目标迭代器位置。
void Print(list<int>& l1, list<int>& l2, list<int>& l3)
{
cout << "l1:";
for (auto e : l1)
{
cout << e << " ";
}
cout << endl;
cout << "l2:";
for (auto e : l2)
{
cout << e << " ";
}
cout << endl;
cout << "l3:";
for (auto e : l3)
{
cout << e << " ";
}
cout << endl;
}
void Test7()
{
list<int> l1;
list<int> l2(3, 7);
list<int> l3(2, 8);
l1.insert(l1.end(), 10);
l1.insert(l1.end(), 20);
l1.insert(l1.end(), 30);
l1.insert(l1.end(), 40);
Print(l1, l2, l3);
// 转移整个链表
l1.splice(l1.begin(), l2);
cout << endl;
Print(l1, l2, l3);
// 转移单个节点
l1.splice(l1.begin(), l3, l3.begin());
cout << endl;
Print(l1, l2, l3);
// 单个链表内的迭代器区间转移
l1.splice(l1.end(), l1, ++l1.begin(), --l1.end());
cout << endl;
Print(l1, l2, l3);
}