STL 学习总结记录

1. 序列

        7种STL容器类型(deque, list, queue, priority_queue, stack, vector和C++11新增的forward_list)都是序列。array也被归类到序列容器。

        序列概念增加了迭代器至少是正向迭代器这样的要求,这保证元素将按特定顺序排列,不会在两次迭代之间发生变化。序列还要求其元素按严格的线性顺序排列。数组和链表都是序列。

        表1-1列出了基本的容器特征。其中,X表示容器类型,如vector;T表示存储在容器中的对象类型;ab表示类型为X的值;r表示类型为X&的值;u表示类型为X的标识符(即,如果x表示vector<int>,则u是一个vector<int>对象)。

表1-1 一些基本的容器特征
表达式返回类型说明复杂度
X::iterator指向T的迭代器类型满足正向迭代器要求的任何迭代器编译时间
X::value_typeTT的类型编译时间
X u;创建一个名为u的空容器固定
X();创建一个匿名的空容器固定
X u(a);调用复制构造函数后u == a线性
X u = a;作用同X u(a);线性
r = a;X&调用赋值运算符后r == a线性
(&a)->~Xvoid对容器中每个元素应用析构函数线性
a.begin()迭代器返回指向容器第一个元素的迭代器固定
a.end()迭代器返回超尾值迭代器固定
a.size()无符号整型返回元素个数,等价于a.end()-a.begin()固定
a.swap(b)void交换a和b的内容固定
a == b可转换为bool如果a和b的长度相同,且a中每个元素都等于(==为真)b中相应的元素,则为真线性
a != b可转换为bool返回 !(a == b)线性

        表1-2列出了序列必须完成的操作。部分表示法与表1-1相同,此外,t表示类型为T(存储在容器中的值的类型)的值,n表示整数,p、q、i 和 j 表示迭代器。

表1-2 序列的要求
表达式返回类型说明
X a(n, t)声明一个名为a的由n个t值组成的序列
X(n, t)创建一个由n个t值组成的匿名序列
X a(i, j)声明一个名为a的序列,并将其初始化为区间 [i, j) 的内容
X(i, j)创建一个匿名序列,并将其初始化为区间 [i, j) 的内容
a.insert(p, t)迭代器将t插入到p的前面
a.insert(p, n, t)void将 n 个 t 插入到 p 的前面
a.insert(p, i, j)void将区间 [i, j) 中的元素插入到p的前面
a.erase(p)迭代器删除p指向的元素
a.erase(p, q)迭代器删除区间 [p, q) 中的元素
a.clear()void等价于erase(begin(), end())

        模板类deque、list、queue、priority_queue、stack和vector都是序列概念的模型,所以它们都支持上表所示的运算符。表1-3列出了其他可能支持的操作。

表1-3 序列的可选要求
表达式返回类型含义容器
a.front()T&*a.begin()vector、list、deque
a.back()T&*--a.end()vector、list、deque
a.push_front(t)voida.insert(a.begin(), t)list、deque
a.push_back(t)voida.insert(a.end(), t)vector、list、deque
a.pop_front(t)voida.erase(a.begin())list、deque
a.pop_back(t)voida.erase(--a.end())vector、list、deque
a[n]T&*(a.begin()+n)vector、deque
a.at(t)T&*(a.begin()+n)vector、deque

        注:a[n]和a.at(n)都返回一个指向容器中第n个元素(从0开始编号)的引用。它们之间的差别在于,如果n落在容器的有效区间外,则a.at(n)将执行边界检查,并引发out_of_range异常。

1.1 vector

(1)vector是数组的一种类表示,它提供了自动内存管理功能,可以动态地改变vector对象的长度,并随着元素的添加和删除而增大和缩小。

(2)它提供了对元素的随机访问。在尾部添加和删除元素的时间是固定的O(1),但在头部或中间插入和删除元素的时间复杂度为线性时间O(N)。

(3)除序列外,vector还是可反转容器的模型。增加两个方法:rbegin():返回一个指向反转序列的第一个元素的迭代器,rend():返回反转序列的超尾迭代器。两种方法返回的迭代器都是类级类型reverse_iterator

1.2 deque

(1)deque模板类表示双端队列,在STL中其实现类似于vector容器,支持随机访问。区别在于:从deque对象的开始位置插入和删除元素的时间是固定的,而不像vector中那样是线性时间。因此,如果多数操作发生在序列的起始和结尾处,则应考虑使用deque数据结构。

(2)deque对象的设计比vector对象更为复杂,因此对元素的随机访问和在序列中部执行线性时间的插入和删除操作时vector容器执行的要快

1.3 list

(1)list模板类表示双向链表。可以双向遍历链表。list和vector之间的关键区别在于,list在链表中任一位置进行插入和删除的时间都是固定的。因此vector强调的是通过随机访问进行快速访问,而list强调的是元素的快速插入和删除。

(2)list也是可反转容器,但不支持数组表示法和随机访问。

(3)与矢量迭代器不同,从容器中插入或删除元素之后,链表迭代器指向元素将不变。在链表中插入新元素并不会移动已有的元素,而只是修改链接信息。指向某个元素的迭代器仍然指向该元素,但它链接的元素可能与以前不同。

(4)除序列和可反转容器的函数外,list模板类还包含了链表专用的成员函数。表1-4列出了一些,通常不必担心Alloc模板参数,因为它有默认值。

表1-4 list成员函数
函数说明
void merge(list<T, Alloc>& x)将链表x与调用链表合并。两个链表必须已经排序。合并后的经过排序的链表保存在调用链表中,x为空。这个链表的复杂度为线性时间。
void remove(const T& val)从链表中删除val的所有实例,这个函数的复杂度为线性时间
void sort()

使用<运算符对链表进行排序;N个元素的复杂度为NlogN

void splice(iterator pos, list<T, Alloc>& x)将链表x的内容插入到pos的前面,x将为空。这个函数的复杂度为固定时间
void unique()连续的相同元素压缩为单个元素。这个函数的复杂度为线性时间。

        程序示例1-1(list.cpp):

// list.cpp -- using a list -- 16.12
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>

void outint(int n) { std::cout << n << " "; }

int main() {
	using namespace std;
	list<int> one(5, 2);  // list 5个2
	int stuff[5] = { 1, 2, 4, 8, 6 };
	list<int> two;
	two.insert(two.begin(), stuff, stuff + 5);
	int more[6] = { 6, 4, 2, 4, 6, 5 };
	list<int> three(two);
	three.insert(three.end(), more, more + 6);

	cout << "List one: ";
	for_each(one.begin(), one.end(), outint);
	cout << endl << "List two: ";
	for_each(two.begin(), two.end(), outint);
	cout << endl << "List three: ";
	for_each(three.begin(), three.end(), outint);
	three.remove(2);
	cout << endl << "List three minus 2s: ";
	for_each(three.begin(), three.end(), outint);
	// insert()和splice()的区别在于:insert()将原始区间的副本插入到目标地址,而splice()将原始区间移到目标地址。
	// 因此,在one的内容与three合并后,one为空。
	// splice()方法执行后,迭代器仍有效。
	three.splice(three.begin(), one);
	cout << endl << "List three after splice: ";
	for_each(three.begin(), three.end(), outint);
	cout << endl << "List one: ";
	for_each(one.begin(), one.end(), outint);
	three.unique();
	cout << endl << "List three after unique: ";
	for_each(three.begin(), three.end(), outint);
	three.sort();
	three.unique();
	cout << endl << "List three after sort & unique: ";
	for_each(three.begin(), three.end(), outint);
	// 非成员函数sort()函数需要随机访问迭代器。因为快速插入的代价是放弃随机访问功能,因此不能将非成员函数sort()用于链表。
	// 因此这个类中包括了一个只能在类中使用的成员版本。
	two.sort();    
	three.merge(two);
	cout << endl << "Sorted two merged into three: ";
	for_each(three.begin(), three.end(), outint);
	cout << endl;

	return 0;
}

1.4 list工具箱

1.5 queue

(1)queue模板类是一个适配器类。queue模板类让底层类(默认为deque)展示典型的队列接口。

(2)queue的限制比deque更多。它不允许随机访问队列元素,甚至不允许遍历队列。它把使用限制在定义队列的基本操作上,可以将元素添加到队尾从队首删除元素查看队首和队尾的值检查元素数目测试队列是否为空。表1-5列出了这些操作。

表1-5 queue的操作
方法说明
bool empty() const

如果队列为空,则返回true;否则返回false

size_type size() const返回队列中元素的数目
T& front()返回指向队首元素的引用
T& back()返回指向队尾元素的引用
void push(const T& x)在队尾插入x
void pop()删除队首元素

1.6 priority_queue

(1)priority_queue模板类是另一个适配器类,它支持的操作与queue相同。区别在于:在priority_queue中,最大的元素被移到队首;内部区别在于,默认的底层类是vector。

(2)可以修改用于确定哪个元素放到队首的比较方式,方法是提供一个可选的构造函数参数:

priority_queue<int> pq1;

priority_queue<int> pq2(greater<int>);

        greater<>()函数是一个预定义的函数对象。

1.7 stack

(1)stack也是一个适配器类,它给底层类(默认情况下为vector)提供了典型的栈接口。

(2)stack不允许随机访问元素,甚至不允许遍历栈。它把使用限制在定义栈的基本操作上,即可以将元素压入栈顶、从栈顶弹出元素、查看栈顶的值、检查元素数目和测试栈是否为空。表1-6列出了这些操作。

表1-6 stack的操作
方法说明
bool empty() const

如果栈为空,则返回true;否则返回false

size_type size() const返回栈中的元素数目
T& top()返回指向栈顶元素的引用
void push(const T& x)在栈顶部插入x
void pop()删除栈顶元素

1.8 array(C++11)

        它并非STL容器,因为其长度是固定的。

2. 关联容器

        关联容器将值与键关联在一起,并使用键来查找值。

        其优点在于,提供了对元素的快速访问。与序列相似,关联容器也允许插入新元素,但不能指定元素的插入位置。原因是关联容器通常有用于确定数据放置位置的算法,以便能够快速检索信息。通常是由某种实现

        STL提供了4种关联容器:set、multiset、map和multimap。前两种在头文件set,后两种在头文件map中定义。

2.1 set

(1)set模拟了多个概念,它是关联集合,可反转、可排序、且键是唯一的,所以不能存储多个相同的值。

set也使用模板参数来指定要存储的值类型:

set<string> A;

第二个模板参数是可选的,可用于指示用来对键进行排序的比较函数或对象。默认情况下,将使用模板less<>。

set<string, less<string> > A;

(2)数学为集合定义了一些标准操作,如并集、交集。STL提供了支持这些操作的算法。所有set对象都自动满足使用这些算法的先决条件。

  • set_union()函数接收5个迭代器参数。

前两个迭代器定义了第一个集合的区间,接下来的连个定义了第二个集合区间,最后一个迭代器是输出迭代器,指出将结果集合复制到什么位置。

例如,要显示集合A和集合B的并集:

set_union(A.begin(), A.end(), B.begin(), B.end(),

                 ostream_iterator<string, char> out(cout, " "));

如果要将结果放在集合C中,则最后一个参数应该是一个指向C的迭代器。显而易见的选择是C.begin(),但它不管用。原因:703

set_union(A.begin(), A.end(), B.begin(), B.end(),

                 insert_iterator<set<string> > (C, C.begin()));

  • 函数set_intersection()和set_difference()分别查找交集和获得两个集合的差,它们的接口与set_union()相同。
  • 方法lower_bound(),upper_bound():

将键作为参数,并返回一个迭代器,该迭代器指向集合中第一个不小于/大于键参数的成员。

2.2 multimap

        与set相似,multimap也是可反转的、经过排序的关联容器,但键和值的类型不同,且同一个键可能与多个值相关联。

multimap<int, string> codes;

        为将信息结合在一起,实际的值类型将键类型和数据类型结合为一对。为此,STL使用模板类pair<class T, class U>将这两种值存储到一个对象中。

法一:创建一个pair,再将它插入:

pair<const int, string> item(213, "Los Angeles");

codes.insert(item);

法二:使用一条语句创建匿名pair对象并将其插入:

codes.insert(pair<const int, string> (213, "Los Angeles"));

(1)成员函数count()接收键作为参数,并返回具有该键的元素数目。

(2)成员函数lower_bound()和upper_bound()将键作为参数,且工作原理与处理set时相同。

(3)成员函数equal_range()用键作为参数,且返回两个迭代器,它们表示的区间与该键匹配。

下面代码打印codes对象中区号为718的所有城市:

pair<multimap<KeyType, string>::iterator, multimap<KeyType, string>::iterator> range = codes.equal_range(718);
cout << "Cities with area code 718: \n";
std::multimap<KeyType, std::string>::iterator it;
for (it = range.first; it != range.second; ++it)
	cout << (*it).second << endl;

3. 无序关联容器(C++11)

        无序关联容器也将值与键关联起来,并使用键来查找值。但底层差别在于,关联容器是基于树结构的,而无序关联容器是基于数据结构哈希表的,这旨在提高添加和删除元素的速度以及提高查找算法的效率。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ STLC++标准模板库(Standard Template Library)的简称。它是C++的一个重要组成部分,提供了一系列的通用模板类和函数,用于处理常见的数据结构和算法问题。 学习C++ STL有以下几个方面的好处: 1. 提高开发效率:STL提供了大量现成的数据结构和算法,比如向量(vector)、链表(list)、队列(queue)、堆栈(stack)等,以及排序、查找、计数、遍历等算法。使用STL可以避免重复造轮子的过程,通过简单的调用就可以快速编写高效的代码,提高开发效率。 2. 提高代码质量:STL是由专业的C++程序员设计和实现的,其设计遵循了面向对象的思想,并使用了模板元编程等技术。使用STL可以提高代码的模块化程度,减少重复代码,使代码更加清晰、简洁和可维护。 3. 为学习其他编程语言打下基础:STL采用了一种通用、抽象的设计,其思想和理念对于学习其他编程语言也是有借鉴意义的。通过学习STL,可以更好地理解数据结构和算法的设计与实现,为学习其他编程语言打下坚实的基础。 要学习C++ STL,可以从以下几个方面入手: 1. 理解STL的组成部分:了解STL的组成部分,包括容器(container)、迭代器(iterator)、算法(algorithm)、函数对象(function object)、适配器(adapter)等。理解它们之间的关系和作用,掌握各个组成部分的用法和特点。 2. 学习STL的常用容器和算法:熟悉STL提供的常用容器和算法,如向量(vector)、链表(list)、队列(queue)、堆栈(stack)等,以及排序、查找、计数、遍历等算法。了解其基本的操作和用法,掌握它们的时间复杂度和使用场景。 3. 理解STL内部实现原理:了解STL内部的实现原理,包括对容器和算法的底层实现,例如迭代器的实现、算法的实现方式和优化等。理解这些原理有助于更好地理解和使用STL,以及优化代码性能。 总之,学习C++ STL对于提高C++编程能力和开发效率非常重要。通过学习STL,可以更好地掌握C++的数据结构和算法,提高代码质量和可维护性,为进一步学习和应用其他编程语言打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值