C++中STL容器之双向链表——list

文章目录

        • 1.list介绍
        • 2.list的成员函数
          • 2.1 构造、拷贝和析构函数
          • 2.2 非变动性操作
          • 2.3 赋值操作
          • 2.4 元素存取操作
          • 2.5 迭代器相关函数
          • 2.6 插入元素
          • 2.6 移除元素
          • 2.7 特殊变动性操作
          • 2.8 排序和翻转操作
        • 3.应用实例
        • 参考资料

1.list介绍

list 是顺序容器的一种,且list 是一个双向链表(double linked list)。双向链表的每个元素都有两个指针域,一个指向直接后继,一个指向直接前驱,如下图所示:
双向链表示意图

  • list的元素可以是任意类型T,但必须是具必须具备赋值和拷贝能力;
  • 必须包含头文件#include
  • 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 c 产生空的list
list c1(c2) 产生同类型的c1,并赋值c2的所有元素
list c(n) 利用类型T的默认构造函数和拷贝构造函数生成一个大小为n的list
list c(n,e) 产生一个大小为n的list,每个元素都是e
list c(begin,end) 产生一个list,以区间[begin,end)为元素初值
~list() 销毁所有元素并释放内存
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
#include //使用list包含的头文件
#include/使用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 0 0

输出要求:
对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号。

输入样例:
6 2
12 4
8 3
0 0

输出样例:
5
1
7

示例程序:

#include
#include //使用list包含的头文件
#include//使用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/

你可能感兴趣的:(#,C++基础知识,c++,STL,双向列表)