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

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/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值