《C++ STL基础及应用》学习笔记(未完待续)

前言

学习这本书已经两三年了,平时在写C++程序的时候,都会时不时的翻阅STL相关的API,已经将这本很适合初学者使用的书籍当作了一本工具书使用。同时,每次在翻看的时候都会得到新的启发和学习。

为方便以后更快,更高效的查阅到相关的知识,特写此学习笔记,将平时经常用到的内容进行简要记录。学习笔记,如有错误,还望指正。

一、STL概述

常用STL包含头文件

  • #include <iterator> 迭代器
  • <iostream> 标准输入输出流
  • <fstream> 文件输入输出流
  • <sstream> 字符串输入输出流
  • <string> 字符串
  • <functional> 函数对象
  • <vector> 向量容器
  • <deque> 双端队列
  • <list> 链表容器
  • <queue> 队列,优先队列
  • <stack> 堆栈
  • <set> 集合、多集合、位集合
  • <map> 映射、多映射
  • <algorithm> 通用算法
  • <numeric> 数值算法

命名空间

标准命名空间

  • using namespace std;

自定义命名空间

// 自定义命名空间示例程序
#include <iostream>
using namespace std;
// 此处定义自己的命名空间
namespace mynamespace{
	void func(){
		cout<<"this is mynamespace!"<<endl;
	}
};
int main(){
	// 使用自定义命名空间,也可像std一样,先进行using namespace后,直接进行函数调用。
	mynamespace::func();
	return 0;
}

二、模板

三、迭代器

1、输入迭代器(Input Iterator)

按顺序只读一次。能进行构造和默认构造。能被复制或赋值,能进行相等性比较,能进行逐步向前移动,能进行读取值。

istream_iterator(): 默认构造器,创建一个流结束的迭代器
istream_iterator(istream &): 参数是输入流。含义是从输入流中读数据,当遇到流结束符时停止。

输入迭代器重载主要操作符

operate	*			访问迭代元素值
operate	++()		前置迭代指针++
operate	++(int)		后置迭代指针++
operate	==			迭代元素相等比较
operate !=			迭代元素不等比较

利用istream_iterator迭代器迭代标准输入流示例代码

#include <iostream>
#include <iterator>
using namespace std;
int main(){
	// 建立键盘输入流,并用istream_iterator枚举整型数据
	istream_iterator<int>	a(cin);
	//	建立输入流结束迭代器
	istream_iterator<int>	b;
	while(1){
		//	调用operator*()	输出数据
		cout<<*a<<endl;
		//	调用operator++(int)	迭代器指针指向下一个元素
		a++;
		//	判断结束
		if(a==b){
			break;
		}
	}
	return 0;
}

2、输出迭代器(Output Iterator)

只写一次。能进行构造或默认构造,能被复制或赋值,能进行相等性比较,能进行逐步向前移动,能进行写入值(*p = x, 但不能读出)。

ostream_iterator(ostream & out): 创建流输出迭代器,用来迭代out输出流。
ostream_iterator(ostream & out, const char * delim): 创建流输出迭代器,用来向out输出流输出数据,输出的数据之间用delim字符串分隔。即每向输出流输出一个数据后,就向输出流输出一个分隔符delim。

输出迭代器重载主要操作符

operate	*			分配迭代元素值空间
operate	=			写入元素值
operate	++()		前置迭代指针++
operate	++(int)		后置迭代指针++

利用ostream_iterator向屏幕输出数据

#include <iostream>
#include <iterator>
using namespace std;
int main(){
	//	创建标准输出迭代器
	ostream_iterator<int>	myout(cout, "\t");
	* myout = 1;
	* myout++;
	return 0;
}

3、前向迭代器(Forward Iterator)

具有输入迭代器、输出迭代器的功能。只能向前移动。

4、双向迭代器(Bidirectional Iterator)

具有前向迭代器的全部功能,另外还可以利用自减操作符 operator-- 向后一次移动一个位置

5、随机访问迭代器(Random Access Iterator)

具有双向迭代器的所有功能,再加上一个指针的所有功能。包括使用操作符operator[] 进行索引,加某个整数值到一个指针就可以向前向后移动若干个位置,或者使用比较运算符在迭代器之间进行比较。

四、输入输出流

1、标准输入输出流

2、文件输入输出流

3、字符串输入输出流

字符串输入输出流类直接对内存而不是对文件和标准输出进行操作,它使用与cin及cout相同的读取和格式化函数进行操纵内存中的数据,头文件<sstream>

  • istringstream: 字符串输入流,提供读string功能
  • ostringstream: 字符串输出流,提供写string功能
  • stringstream: 字符串输入输出流,提供读写string功能

stringstream进行类型转换示例:

#include <iostream>
#include <sstream>
using namespace std;
int main(){
	string data = "123 456 789";
	stringstream ss;
	ss<<data;
	for(int i=0;i<3;i++){
		int tmp;
		ss>>tmp;
		cout<<tmp<<endl;
	}
	return 0;
}

五、字符串

六、函数对象

七、通用容器

1、概述

容器分类:

1)序列性容器

按照线性排列来存储某类型值的集合,每个元素都有自己特定的位置,顺序容器主要有vector, deque, list

vector:动态数组。它是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放。新值大于当前大小时才会再分配内存。对最后元素操作最快(在后面添加删除最快),此时一般不需要移动内存,如果元素是结构或是类,移动的同时还会进行构造和析构操作。vector的一大特点是可直接访问任何元素。

deque:与vector类似,支持随机访问和快速插入删除,它在容器中的某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入、删除数据。由于它主要对前端、后端进行操作,因此也叫做双端队列。

list:又叫链表,是一种双线性列表,只能顺序访问(从前向后或者从后向前),与前面两种容器类有一个明显的区别就是它不支持随机访问。要访问表中某个下标处的项需要从头或表尾处(接近该下标的一端)开始循环。

2)关联式容器

与前面的顺序性容器相比,关联式容器更注重快速和高效地检索数据的能力。这些容器是根据键值来检索数据的,键可以是值也可以是容器中的某一成员。这一类的成员在初始化后都是按一定的顺序排好序的。关联式容器主要有set, multiset, map, multimap。

set: 快速查找,不允许重复值

multiset: 快速查找,允许重复值

map:一对一映射,基于关键字快速查找,不允许重复值。

multimap:一对多映射,基于关键字快速查找,允许重复值。

3)容器适配器

对已有的容器进行某些特性的再封装,不是一个真正的新容器。主要有stack、queue。

stack:堆栈类,特点是后进先出。

queue:队列类,特点是先进先出。

容器共性:

容器一般来说都有下列函数

  • 默认构造函数:提供容器默认初始化的构造函数
  • 复制构造函数:将容器初始化为现有同类容器副本的构造函数
  • 析构函数:不在需要容器时进行内存整理的析构函数。
  • empty:容器中没有元素时返回true,否则返回false。
  • max_size:返回容器中最大元素个数。
  • size:返回容器中当前元素个数。
  • operator = :将一个容器赋给另一个容器。
  • operator < : 如果第一个容器小于第二个容器,返回true,否则返回false。
  • operator <= : 如果第一个容器小于或等于第二个容器,返回true,否则返回false。
  • operator > : 如果第一个容器大于第二个容器,返回true,否则返回false。
  • operator >= : 如果第一个容器大于或等于第二个容器,返回true,否则返回false。
  • operator == : 如果第一个容器等于第二个容器,返回true,否则返回false。
  • operator != : 如果第一个容器不等于第二个容器,返回true,否则返回false。
  • swap: 交换两个容器的元素

顺序容器和关联容器共有函数

  • begin:该函数有两个版本,返回iterator或const_iterator,返回容器第一个元素迭代器指针。
  • end:该函数有两个版本,返回iterator或const_iterator,返回容器最后一个元素迭代器指针。
  • rbegin:该函数有两个版本,返回reverse_iterator或const_reverse_iterator,返回容器最后一个元素迭代器指针。
  • rend:该函数有两个版本,返回reverse_iterator或const_reverse_iterator,返回容器首个元素前面一位的迭代器指针。
  • erase: 从容器中清楚一个或多个元素。
  • clear: 清楚容器中所有元素。

容器比较

vector:(连续的空间存储,可以使用[]操作符)快速地访问随机的元素,快速地在末尾插入元素,但是在序列中随机插入、删除元素比较慢。而且如果一开始分配的空间不够的话,有一个重新分配更大空间的过程。

deque:(小片的连续,小片间用链表相连,实际上内部有一个map指针,因为知道类型,所以还是可以使用[],只是速度没有vector快)快速地访问随机的元素,快速地在开始和末尾插入元素,随机地插入、删除数据要慢,空间的重新分配要比vector快,重新分配空间后,原有的元素不需要备份。对deque的排序操作,可将deque先赋值到vector,排序后再复制回vector。

list:(每个元素间用链表相连)访问随机元素不如vector快,随机地插入元素比vector快,对每个元素分配空间,所以不存在空间不足,重新分配的情况。

set: 内部元素唯一,用一颗平衡树结构来存储,因此遍历的时候就排序了,查找也比较快。

map:一对一地映射结合,key不能重复。

2、vector容器

vector类称作向量类,它实现了动态的数组,用于元素数量变化的对象数组。像数组一样,vector类也用从0开始的下标表示元素的位置;但和数组不同的是,当vector对象创建后,数组的元素个数会随着vector对象元素个数的增大和缩小而自动变化。

vector常用函数:

1)构造函数

  • vector():创建一个空vector
  • vector(int nSize):创建一个vector,元素个数为nSize
  • vector(int nSize, const T & t):创建一个vector,元素个数为nSize,且值均为t
  • vetor(const vector& ):复制构造函数

2)增加函数

  • void push_back(const T & x):向量尾部增加一个元素x
  • iterator insert(iterator it, const T& x):向量中某一个元素前增加一个元素x
  • void insert(iterator it, int n, const T& x):向量中某一个元素前增加n个相同元素x
  • void insert(iterator it, const_iterator first, const_iterator last):向量中某一个元素前插入另一个相同类型向量的[first, last)间的数据

3)删除函数

  • iterator erase(iterator it):删除向量中某一个元素
  • iterator erase(iterator first, iterator last):删除向量中[first,last)中元素
  • void pop_back():删除向量中最后一个元素
  • void clear():删除向量中所有元素

4)遍历函数

  • reference at(int pos):返回pos位置元素的引用
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用
  • iterator begin():返回向量头指针,指向第一个元素
  • iterator end():返回向量尾指针,不包括最后一个元素,在其下面
  • reverse_iterator rbegin():反向迭代器,最后一个元素迭代指针
  • reverse_iterator rend():反向迭代器,第一个元素之前的迭代指针

5)判断函数

  • bool empty() const:向量是否为空,若true,则向量中无元素

6)大小函数

  • int size() const:返回向量中元素个数
  • int capacity() const:返回当前向量所能容纳的最大元素值
  • int max_size() const:返回最大可允许的vector元素数量值

7)其他函数

  • void swap(vector &):交换两个同类型向量的数据
  • void assign(int n, const T& x):设置容器大小为n个元素每个元素值为x
  • void assign(const_iterator first, const_iterator last):容器中[first,last)中元素设置为当前向量元素

vector使用示例:

#include <iostream>
// 引入头文件 
#include <vector>
using namespace std;
int main(){
	// 初始化 
	vector<int> vec;
	
	// 新增元素
	vec.push_back(1);
	vec.push_back(2);	// vec:1,2
	for(int i=0; i<10; i++){
		vec.push_back(i);
	} 
	
	// 获取向量元素数
	int nSize = vec.size();
	
	// 通过数组方式访问
	for(int i=0; i<nSize; i++){
		cout<<vec[i]<<endl;
	} 
	// 通过获取引用访问
	for(int i=0; i<nSize; i++){
		int &nValue = vec.at(i);
		cout<<nValue<<endl;
	} 
	// 通过迭代器访问
	vector<int>::iterator vec_it = vec.begin();
	while(vec_it != vec.end()){
		cout<< *vec_it <<"\t";
		vec_it++;
	} 
	
	// 通过数组下标方式修改
	vec[0] = 10;
	// 通过引用修改
	int& m = vec.at(0);
	m = 20;
	// 通过迭代器修改
	vector<int>::iterator vec_it_1 = vec.begin();
	*vec_it_1 = 30;
	
	// 删除第一个元素
	vec.erase(vec.begin());
	// 删除第2~5个元素,[vec.begin()=1, vec.begin()+5) 左闭右开 
	vec.erase(vec.begin()+1, vec.begin()+5); 
	// 删除尾部元素
	vec.pop_back();
	
	return 0;
} 
3、deque容器

deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。

常用函数:

1)构造函数

  • deque():创建一个空deque
  • deque(int nSize):创建一个deque,元素个数为nSize
  • deque(int nSize, const T& t):创建一个deque,元素个数为nSize,且值均为t
  • deque(const deque& ):复制构造函数

2)增加函数

  • void push_front(const T& x):双端队列头部增加一个元素x
  • void push_back(const T& x):双端队列尾部增加一个元素x
  • iterator insert(iterator it, const T& x):双端队列中某一元素前增加一个元素x
  • void insert(iterator it, int n, const T& x):双端队列中某一元素前增加n个相同元素x
  • void insert(iterator it, const_iterator first, const_iterator last):双端队列中某一元素前插入另一个相同类型向量的[first, last)间的数据

3)删除函数

  • iterator erase(iterator it):删除双端队列中某一个元素
  • iterator erase(iterator first, iterator last):删除双端队列中[first, last)中元素
  • void pop_front():删除双端队列中最前一个元素
  • void pop_back():删除双端队列中最后一个元素
  • void clear():删除双端队列中所有元素

4)遍历函数

  • reference at(int pos):返回pos位置元素的引用
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用
  • iterator begin():返回向量头指针,指向第一个元素
  • iterator end():返回向量尾指针,不包括最后一个严肃,在其下面
  • reverse_iterator rbegin():反向迭代器,最后一个元素的迭代指针
  • reverse_iterator rend():反向迭代器,第一个元素前的迭代指针

5)判断函数

  • bool empty() const:向量是否为空,若true,则向量中无元素

6)大小函数

  • int size() const:返回向量元素个数
  • int max_size() const:返回最大可允许的双端队列元素数量值

7)其他函数

  • void swap(vector &):交换两个同类型向量的数据
  • void assign(int n, const T& x):设置容器大小为n个元素,每个元素值为x
  • void assign(const_iterator first, const_iterator last):容器中[first, last)中元素设置成当前双端队列元素

deque使用示例:

// deque容器与vector容器许多动能相似,此处代码主要示例不同之处。
#include <iostream>
// 引入头文件 
#include <deque>
using namespace std;
int main(){
	deque<int> dq;
	// 尾部增加元素 
	dq.push_back(1);
	// 头部增加元素 
	dq.push_front(2);	// 2,1
	
	// 头部删除元素
	dq.pop_front();		//1
	 
	return 0;
}

在建立vector容器时,一般来说伴随着建立空间——填充数据——重建更大空间——复制原空间数据——删除原空间——添加新数据,如此反复,保证vector始终是一块独立的连续内存空间

在建立deque容器时,一般伴随着建立空间——填充数据——建立新空间——填充新数据,如此反复,没有原空间数据的复制和删除,是由多个分段连续的内存空间组成的。

4、list容器

相对于vector的连续线性空间,list是一个双向链表,他有一个重要性质:插入操作和删除操作都不会造成原有的list迭代器失效,每次插入和删除一个元素就配置或释放一个元素空间。也就是说,对于任何位置的元素插入和删除,list永远是常数时间。

常用函数

1)构造函数

  • list<Elem> c:创建一个空的list
  • list<Elem> c1(c2):复制另一个同类型元素的list
  • list<Elem> c(n):创建n个元素的list,每个元素值由默认构造函数确定
  • list<Elem> c(n,elem):创建n个元素的list,每个元素值为elem
  • list<Elem> c(begin,end):由迭代器创建list,迭代区间为[begin, end)

2)大小、判断空函数

  • int size() const:返回容器元素个数
  • bool empty() const:判断容器是否空,若返回true,表名容器已空

3)增加、删除函数

  • void push_back(const T& x):list容器尾元素后增加一个元素x
  • void push_front(const T& x):list容器首元素前增加一个元素x
  • void pop_back():删除容器尾元素,当且仅当容器不为空
  • void pop_front():删除容器首元素,当且仅当容器不为空
  • void remove(const T& x):删除容器中所有元素值等于x的元素
  • void clear():删除容器中所有元素
  • iterator insert(iterator it, const T& x=T()):迭代器指针it前插入元素x,返回x迭代器指针
  • void insert(iterator it, size_type n, const T& x):在迭代器指针it前插入n个相同元素x
  • void insert(iterator it, const_iterator first, const_iterator last):把[first, last)间的元素插入迭代指针it前
  • iterator erase(iterator it):删除迭代器指针it对应的元素
  • iterator erase(iterator first, iterator last):删除迭代器指针[first, last)间的元素

4)遍历函数

  • iterator begin():返回首元素的迭代器指针
  • iterator end():返回尾元素后的迭代器指针,而不是尾元素的迭代器指针
  • reverse_iterator rbegin():返回尾元素的逆向迭代器指针,用于逆序遍历容器
  • reverse_iterator rend():返回首元素前的逆向迭代器指针,用于逆序遍历容器
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用

5)操作函数

  • void sort():容器内所有元素排序,默认是升序
  • template<class Pred> void sort(Pred pr):容器内所有元素根据预判定函数pr排序
  • void swap(list& str):两list容器交换功能
  • void unique():容器内相邻元素若有重复的,则仅保留一个
  • void splice(iterator it, list& x):队列合并函数,队列x所有元素插入迭代器指针it前,x变成空队列
  • void splice(iterator it, list& x, iterator first):队列x中移走[first, end)间元素插入迭代指针it前
  • void splice(iterator it, list& x, iterator first, iterator last):队列x中移走[first, last)间元素插入迭代指针it前
  • void reverse():反转容器中元素顺序

list使用示例

#include <iostream>
#include <string>
// 引入头文件 
#include <list>
using namespace std;
int main(){
	list<string> test;
	// 尾部添加元素 
	test.push_back("back");
	// 首部添加元素
	test.push_front("front");
	
	// 访问首部元素
	cout<<test.front()<<endl;
	cout<<*test.begin()<<endl;
	
	// 访问尾部元素
	cout<<test.back()<<endl;
	cout<<*(test.rbegin())<<endl;
	
	// 删除首部元素
	test.pop_front();
	
	// 删除尾部元素
	test.pop_back();
	
	list<int> list_i;
	for(int i=0; i<5; i++){
		list_i.push_back(10 * i);
	} 
	
	// 迭代器遍历
	list<int>::iterator it = list_i.begin();
	for(; it!=list_i.end(); it++){
		cout<<*it<<endl;
	} 
	// 逆序迭代器遍历
	list<int>::reverse_iterator rit = list_i.rbegin();
	for(; rit!=list_i.rend(); rit++){
		cout<<*rit<<endl;
	} 
	
	// 排序
	list_i.sort();
	 
	return 0;
}

注:当list的每个元素为string的时候,采用迭代器进行遍历,it++并不是跳到下一个string,而是string的下一个字符。

5、队列和堆栈

队列和堆栈是常用和重要的数据结构。队列只允许在表的一端插入,在另一端删除,允许插入的一端叫做队尾,允许删除的一端叫做队头,是一种先进先出线性表。栈只允许在表的一端进行插入和删除操作,是一种后进先出的线性表。

常用函数:

1)构造函数

  • queue(class T, class Container=deque<T>):创建元素类型为T的空队列,默认容器是deque
  • stack(class T, class Container=deque<T>):创建元素类型为T的空堆栈,默认容器是deque

2)操作函数

队列和堆栈共有函数:

  • bool empty():如果队列(堆栈)为空返回true,否则返回false
  • int size():返回队列(堆栈)中元素数量
  • void push(const T& t):把t元素压入队尾(栈顶)
  • void pop():当队列(栈)非空情况下,删除队头(栈顶)元素

队列独有函数:

  • T& front():当队列非空情况下,返回队头元素引用
  • T& back():当队列非空情况下,返回队尾元素引用

堆栈独有函数:

  • T& top():当栈非空情况下,返回栈顶元素的引用

操作代码示例

#include <iostream>
// 引入堆栈头文件 
#include <stack>
// 引入队列头文件
#include <queue> 
#include <vector>
#include <deque>
using namespace std;
int main(){
	// 底层容器采用vector 
	stack<int, vector<int> > s;
	// 插入操作 
	for(int i=0; i<10; i++){
		s.push(i);
	} 
	// 取栈顶
	cout<<s.top()<<endl;
	// 出栈
	s.pop();
	
	// 底层容器采用deque
	queue<int, deque<int> > dq;
	for(int i=0; i<10; i++){
		// 入队列 
		dq.push(i);
	} 
	//取队头 
	cout<<dq.front()<<endl; 
	// 取队尾
	cout<<dq.back()<<endl;
	// 出队
	dq.pop(); 
	return 0;
}
6、优先队列

优先队列即priority_queue类,带优先权的队列,优先权高的元素优先出队。与普通队列相比,共同点都是对队头做删除操作,队尾做插入操作,但不一定遵循先进先出原则,也可能先进后出。prority_queue是一个基于某个基本序列容器进行构建的适配器,默认的序列容器是vector。

常用函数:

1)构造函数

  • priority_queue(const Pred& pr=Pred(), const allocator_type& al=allocator_type()):创建元素类型为T的空优先队列,Pred是二元比较函数,默认是class<T>
  • prority_queue(const value_type*first, const value_type * last, const Pred& pr=Pred(), const allocator_type& al=allocator_type()):以迭代器[first, last)指向元素,创建元素类型为T的优先队列,Pred是二元比较函数,默认是less<T>

2)操作函数

  • bool empty():如果优先队列为空返回true,否则返回false
  • int size():返回优先队列中元素数量
  • void push(const T& t):把t元素压入优先队列
  • void pop():优先队列非空的情况下,删除优先级最高元素
  • T& top():优先队列非空情况下,返回优先级最高元素的引用

使用代码示例

#include <iostream>
//引入头文件 
#include <queue>
using namespace std;
int main(){
	int a[] = {1,2,3,4,5,6,7,8,9,10};
	// 通过构造函数将数组a的元素送入优先队列
	priority_queue<int> pr(a,a+10);
	// 入队列
	pr.push(11);
	while(!pr.empty()){
		// 取队头 
		cout<<pr.top()<<endl;
		// 出队
		pr.pop(); 
	}
	return 0; 
}

如果是自定义的比较函数,通常定义class,然后重载operator < 。
或者进行书写二元比较函数,在进行定义的时候,传入其中。

7、bitset容器
8、集合
9、映射

八、非变异算法

九、变异算法

十、排序及相关操作

十一、STL应用

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值