文章目录
1.list介绍
list 是顺序容器
的一种,且list 是一个双向链表(double linked list)
。双向链表的每个元素都有两个指针域,一个指向直接后继,一个指向直接前驱,如下图所示:
- list的元素可以是任意类型T,但必须是具必须具备
赋值和拷贝
能力; - 必须包含头文件
#include <list>
; - list
不支持随机存取
,因此不提供下标操作符,而且不能使用STL中算法sort对其进行排序。因此,list容器引入了sort成员函数以完成其排序; - 在任何位置上执行元素的安插和移除都很快;
- 安插和删除不会导致指向其他元素的指针、引用、迭代器失效。
2.list的成员函数
llist的构造函数和许多成员函数都与vector类似。除了顺序容器都有的成员函数外,list还有一些特有的成员函数。
list相比较vector的一个很大的优点,vector 的 erase 操作牵涉元素的移动,不能在常数时间内完成,所花费的时间和容器中的元素个数有关;而 list 的 erase 操作只是修改几个指针而已,可以在常数时间内完成。
也就是所,在 list 容器中,在已经定位到要增删元素的位置的情况下,增删元素能在常数时间内完成。
如下图在 ai 和 ai+1 之间插入一个元素,只需要修改 ai 和 ai+1 中的指针即可。
复杂度
操作 | 复杂度 |
---|---|
push_back() | O(1) |
pop_back() | O(1) |
push_front() | O(1) |
pop_front() | O(1) |
insert() | O(1) |
erase() | O(1) |
访问 | O(n) |
注释
- 添加和插入元素的效率较高,因为不涉及元素的移动,用来做随机插入和删除操作容器,例如队列和栈的容器;
- 访问开始和末尾的元素最快,其他元素的访问都是O(n),因为都需要从头开始遍历;
- 如果经常进行添加和删除操作并且不经常随机访问的话,使用list。
2.1 构造、拷贝和析构函数
操作 | 含义 |
---|---|
list<T> c | 产生空的list |
list<T> c1(c2) | 产生同类型的c1,并赋值c2的所有元素 |
list<T> c(n) | 利用类型T的默认构造函数和拷贝构造函数生成一个大小为n的list |
list<T> c(n,e) | 产生一个大小为n的list,每个元素都是e |
list<T> c(begin,end) | 产生一个list,以区间[begin,end)为元素初值 |
~list<T>() | 销毁所有元素并释放内存 |
2.2 非变动性操作
操作 | 含义 |
---|---|
c.size() | 返回容器中实际数据的个数 |
c.empty() | 判断容器是否为空 |
c.max_size() | 返回容器最大容纳数据的数量(固定值) |
2.3 赋值操作
操作 | 含义 |
---|---|
c1 = c2 | 将c2的全部元素赋值给c1 |
c.assign(n,elem) | 将n个elem的拷贝赋值给c |
c.assign(beg,end) | 将[beg; end)区间中的数据赋值给c |
c1.swap(c2) | 将c1和c2元素互换 |
swap(c1,c2) | 同上,全局函数 |
2.4 元素存取操作
操作 | 含义 |
---|---|
c.front() | 传回第一个数据的引用,不检查元素是否存在 |
c.back() | 传回最后一个数据引用,不检查这个数据是否存在 |
2.5 迭代器相关函数
操作 | 含义 |
---|---|
c.begin() | 返回一个迭代器,指向第一个元素(即是指向第一个数据的一个指针) |
c.end() | 返回一个迭代器(指针),指向最后一个元素的下一个位置(实际不存在) |
c.rbegin() | 返回一个逆向迭代器,指向逆向遍历的第一个元素 |
c.rend() | 返回一个逆向迭代器,指向最后一个数据的下一个位置 |
2.6 插入元素
操作 | 含义 |
---|---|
c.insert(pos,elem) | 在pos位置插入一个elem拷贝,传回新数据位置 |
c.insert(pos,n,elem) | 在pos位置插入n个elem数据,无返回值 |
c.insert(pos,beg,end) | 在pos位置插入在[beg,end)区间的数据。无返回值 |
c.push_back(elem) | 在尾部加入一个元素elem的副本 |
c.push_front(elem) | 在头部添加一个元素elem的副本 |
2.6 移除元素
操作 | 含义 |
---|---|
c.pop_back() | 删除最后一个数据,但不返回 |
c.pop_front() | 删除第一个元素,但不返回 |
c.erase(pos) | 删除pos位置的数据,传回下一个数据的位置 |
c.erase(beg,end) | 删除[beg,end)区间的数据,传回下一个数据的位置 |
c.clear() | 移除容器中所有数据 |
c.resize(num) | 将元素数量改为num(增加的元素默认初始化,多余的元素被删除) |
c.resize(num.e) | 将元素数量改为num(增加的元素是e的副本) |
c.remove(val) | 移除所有值为val的元素 |
c.remove_if(op) | 移除所有"op(val)==true"的元素val |
2.7 特殊变动性操作
操作 | 含义 |
---|---|
c.unique | 移除重复元素,只保留一个 |
c.unique(op) | 移除op(val)结果为true的重复元素 |
c1.splice(pos,c2) | 将c2内所有元素转移到c1的迭代器pos之前 |
c1.splice(pos,c2,c2pos) | 将c2内c2pos所指的元素转移到c1内pos之前 |
c1.splice(pos,c2,c2beg,c2end) | 将c2内[c2begin,c2end)区间内所有元素转移到c2的pos之前 |
2.8 排序和翻转操作
操作 | 含义 |
---|---|
c.sort() | 以operator < 为准则对所有元素排序 |
c.sort(op) | 以op为排序准则对所有元素排序 |
c. merge(c2) | 假设c1和c2已有序,将c2全部元素转移到c1并保证合并后的list仍为有序,并清空c2 |
c.reverse() | 将所有元素反序 |
简单示例代码:
#include<iostream>
#include<list> //使用list包含的头文件
#include<algorithm>/使用STL中的算法需要包含此头文件
using namespace std;
//打印列表
void showlist(list<int>& l) {
for (list<int>::iterator it = l.begin(); it != l.end(); it++) cout << *it << " ";
cout << endl;
}
int main()
{
int a[5] = { 1, 3, 2, 4, 2 };
int b[7] = { 10, 30, 20, 30, 30, 40, 40 };
list<int> list1(a, a + 5), list2(b, b + 7); //赋值数组
list1.sort();//列表排序
cout << "1)"; showlist(list1); //输出:1)1 2 2 3 4
list1.remove(2); //删除所有和A(2)相等的元素
cout << "2)"; showlist(list1); //输出:2)1 3 4
list2.pop_front(); //删除第一个元素
cout << "3)"; showlist(list2); //输出:3)30 20 30 30 40 40
list2.unique(); //删除所有和前一个元素相等的元素
cout << "4)"; showlist(list2); //输出:4)30 20 30 40
list2.sort();
list1.merge(list2); //合并 lst2 到 lst1 并清空 lst2
cout << "5)"; showlist(list1); //输出:5)1 3 4 20 30 30 40
cout << "6)"; showlist(list2); //lst2是空的,输出:6)
list1.reverse(); //将 lst1 前后颠倒
cout << "7)"; showlist(list1); //输出 7)40 30 30 20 4 3 1
list2.insert(list2.begin(), a + 1, a + 4); //在 lst2 中插入 3,2,4 三个元素
list <int>::iterator p1, p2, p3;
p1 = find(list1.begin(), list1.end(), 30);
p2 = find(list2.begin(), list2.end(), 2);
p3 = find(list2.begin(), list2.end(), 4);
list1.splice(p1, list2, p2, p3); //将[p2, p3)插入p1之前,并从 lst2 中删除[p2,p3)
cout << "8)"; showlist(list1); //输出:8)40 2 30 30 20 4 3 1
cout << "9)"; showlist(list1); //输出:9)3 4
system("pause");
return 0;
}
程序输出:
1)1 2 2 3 4
2)1 3 4
3)30 20 30 30 40 40
4)30 20 30 40
5)1 3 4 20 30 30 40
6)
7)40 30 30 20 4 3 1
8)40 2 30 30 20 4 3 1
9)40 2 30 30 20 4 3 1
请按任意键继续. . .
3.应用实例
约瑟夫问题
是:有 n 只猴子,按顺时针方向围成一圈选大王(编号为 1~n),从第 1 号开始报数,一直数到 m,数到 m 的猴子退到圈外,剩下的猴子再接着从 1 开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王。编程求输入 n、m 后,输出最后猴王的编号。
输入数据:
每行是用空格分开的两个整数,第一个是 n,第二个是 m(0<m, n<=1 000 000)。最后一行是:
0 0
输出要求:
对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号。
输入样例:
6 2
12 4
8 3
0 0
输出样例:
5
1
7
示例程序:
#include<iostream>
#include<list> //使用list包含的头文件
#include<algorithm>//使用STL中的算法需要包含此头文件
using namespace std;
int main()
{
list<int> monkeys;
int n, m;
while (1) {
cin >> n >> m;
if (n == 0 && m == 0) break;
monkeys.clear(); //每次新输入,清空列表
//将猴子编号加入列表
for (int i = 1; i <= n; i++) monkeys.push_back(i);
list<int>::iterator it = monkeys.begin();
while (monkeys.size() > 1) { //只要还有不止一只猴子,就要找一只猴子让其出列
for (int i = 1; i < m; ++i) { //报数
++it;
if (it == monkeys.end())
it = monkeys.begin();
}
//删除第m只猴子
it = monkeys.erase(it); //删除元素后,迭代器失效,
//要重新让迭代器指向被删元素的后面
if (it == monkeys.end())
it = monkeys.begin();
}
cout << monkeys.front() << endl; //front返回第一个元素的引用
}
system("pause");
return 0;
}
参考资料
1.STL教程:C++ STL快速入门(非常详细)
http://c.biancheng.net/stl/