最近在准备一个机试,就火速入门了C++ STL,来快速解决一些经典问题。在此做简单记录,目的是今后能用这篇文章快速捡起STL的最基本用法。笔记几乎全部来自《算法笔记》,题目来自codeup和pat。


目录

vector
set
string
map
queue
priority_queue
stack
pair
algorithm头文件下的函数

1 vector

变长数组

1.1 定义

#include<vector>
using namespace std;
vector<int> name;
vector<node> name;	// 节点数组
vector<vector<int> > name;	//二维数组
vector<int> vi[100];	//vector数组,长度100,每个vector变长

1.2 元素访问

vector可以用下标和迭代器访问。

vector<int> vname;
vname.push_back(1);
vname.push_back(1);
vname.push_back(1);
vname.push_back(1);

int x = vname[0];	// 下标访问

vector<int>::iterator it = vname.begin();
int x = *it;	// 迭代器访问
int y = *(it+3);

在常用STL容器里,只有在vector和string中,允许使用 name.begin()+3 这种迭代器加整数的访问方式。

1.3 常用函数

  • push_back(x):容器尾插入元素
  • pop_back():容器尾部移除元素
  • size()
  • clear()
  • insert(it, x):在迭代器处插入元素
  • erase(it):删除迭代器处元素
  • erase(first, last):删除[first, last)元素

1.4 vector常见用途

  • 代替数组
  • 用邻接表存储图

2 set

自动排序、不含重复元素的容器

2.1 定义

#include<set>
using namespace std;
set<int> name;
set<node> name;
set<int> a[100];	//100个set容器组成的数组

2.2 元素访问

与vector不同,set只能用迭代器访问。

set<int> name;
set.insert(3);

set<int>::iterator it = name.begin();
int x = *it;	// 迭代器访问

2.3 常用函数

  • insert(x):插入元素
  • find(value):返回set中对应值为value的迭代器
  • size()
  • clear()
  • erase(it):删除迭代器处元素
  • erase(value):删除值为value的元素
  • erase(first, last):删除[first, last)内所有元素

2.4 set常见用途

  • 自动去重并按升序排序

3 string

字符串

3.1 定义

#include<set>
using namespace std;
string str1;
string str2 = "abc";

3.2 元素访问

string容器可以用下标或迭代器访问。

string str = "abcd";
for(int i=0;i<str.length();i++){
	c = str[i];	// 下标访问
}
for(string::iterator it = str.begin();it!=str.end();it++){
	c = *it;	//迭代器访问。一般用不到,但erase()和insert()要求以迭代器作为参数。
}

输出方式

// 读入和输出整个字符串,只能用cin和cout
cin>>str;
cout<<str;
// c_str()将字符串转换成字符数组,用于printf输出
printf("%s", str.c_str());

3.3 常用函数

  • 加法运算符:+ 和 +=
  • 比较运算符:=、!=、<、>=等。”aa”<”aaa”,”b”>”a”
  • size()或length()
  • clear()
  • insert(pos, string):在pos位置插入string,pos是下标
  • insert(it, it2, it3):把字符串[it2, it3)插入到it位置
  • erase(it):删除迭代器处字符
  • erase(first, last):删除[first, last)内所有字符
  • erase(pos, length):删除pos开始length个字符
  • substr(pos, len):返回从pos开始length长度的子串
  • find(str):如果str是字符串的子串,返回其第一次出现的位置。如果不是子串,返回string::npos。
  • string::npos:作为find函数失配时的返回值
  • replace(pos, len, str2):从pos位置开始len长度的子串替换为str2
  • replace(it1, it2, str2):[it1, it2)子串替换为str2

4 map

映射

4.1 定义

#include<map>
using namespace std;
map<string, int> mp;	// string到int的映射
map<set<int>, string> mp;	// set到string的映射

4.2 元素访问

用下标访问map,可以参考数组(int到int的映射)的访问的格式。

map<char, int> mp;
mp['c'] = 20;
c = mp['c'];	// 用下标访问

用迭代器访问map,通过一个it访问键和值两个元素。it->first访问键,it->second访问值。

map<char, int> mp;
mp['b'] = 120;
mp['a'] = 100;
mp['c'] = 140;
for(map<char, int>::iterator it = mp.begin(); it!= mp.end(); it++){
	printf("%c %d\n",it->first, it->second);
}

输出结果:

a 100
b 120
c 140
map内部会以键值从小到大自动排序。map和set内部都是使用红黑树实现的。

4.3 常用函数

  • find(key):返回键为key的迭代器 mp.find('a')
  • 查找:if(mp.find(key)!=mp.end())
  • size()
  • clear()
  • erase(it):删除迭代器处元素
  • erase(key):删除值key为键的元素
  • erase(first, last):删除[first, last)内所有元素

4.4 map常见用途

  • 需要建立映射的场合
  • 判断大整数或其他类型数据是否存在,可以把map当成bool数组使用

5 queue

队列

5.1 定义

#include<queue>
using namespace std;
queue<int> name;

5.2 元素访问

只能通过front()来访问队首元素,或者back()访问队尾元素。

queue<int> q;
q.push(1);
q.push(2);
q.push(3);
printf("%d %d", q.front(), q.back());

输出结果

1 3

比较好记:谁先来排队,谁就站在队头。

5.3 常用函数

  • push(x):x入队
  • front(), back():返回队首和队尾元素
  • pop():队首元素出队
  • empty():检测是否为空
  • size()

5.4 queue常见用途

  • 广度优先搜索
  • 注意:使用front()和pop()函数前,先用empty()判断非空。

6 priority_queue

内部按照优先级排序的队列

6.1 定义

#include<queue>
using namespace std;
priority_queue<typename> name;

6.2 元素访问

优先队列没有front()函数和back()函数,只能通过top()函数访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。

printf("%d", name.top());

6.3 常用函数

  • name.push(x);将x入队,复杂度O(logN)
  • name.pop();队首元素出队,复杂度O(logN)
  • typename x = name.top();获得队首元素,复杂度O(1)
  • name.empty();
  • name.size()

6.4 元素优先级设置

6.4.1 基本数据类型的优先级设置

对于int型、double型、char型,优先队列的默认设置是数字越大优先级越高。

也可以在定义过程中设置优先队列的优先级:

priority_queue<int, vector<int>, greater<int> > pq;

其中vector<int>填写的是来承载底层数据结构heap的容器;第三个参数greater<int>则是对第一个参数的比较类,less<int>表示数字大的优先级大,greater<int>表示数字小的优先级大。

6.4.2 结构体的优先级设置
struct Student{
    string name;
    int score;
    friend bool operator < (Student s1, Student s2){
        return s1.score < s2.score;
    }
};

定义友元函数,重载“<”运算符(重载大于号会编译错误,其他符号可以由小于号代替,即s1 > s2等价于判断s2 < s1,s1 == s2等价于判断!(s1 < s2)&&!(s2 < s1))。

函数内部“return s1.score < s2.score”,重载后小于号还是小于号的作用。此时Student类型的优先队列,内部是分数高的学生优先级高。

priority_queue<Student> pq;

同理,如果想要低分优先级高,把return中的小于号改为大于号即可。

6.4.3 与sort()的比较

优先队列的重载和排序函数类似,都是由两个变量作为参数,return了true或false作为大小关系。

效果上,大小规则是相反的。在sort()中,如果return s1.score>s2.score,是按照分数由大到小排列;在优先队列中,则是把小的分数元素放到队首。原因在于:优先队列默认规则是大的数优先级高,重载小于号为相反(s1.score>s2.score),只是把默认的规则反向了一下。

有没有办法跟sort()中的cmp函数那样写在结构体外面呢?

struct Student{
    string name;
    int score;
};
struct cmp{
    bool operator () (Student s1, Student s2){
        return s1.score > s2.score;
    }
};

在这种情况下,使用第二种定义方法定义优先队列:

priority_queue<Student, vector<Student>, cmp> pq;

即便是基本数据类型或者其他STL容器(如set),也可以通过以上方法定义优先级。

6.4.4 引用

最后,如果结构体内的数据较为庞大(例如出现了数组、字符串),可以使用引用来提高效率。

struct Student{
    string name;
    int score;
    friend bool operator < (const Student &s1, const Student &s2){
        return s1.score > s2.score;
    }
};
priority_queue<Student> pq;

struct Student{
    string name;
    int score;
};
struct cmp{
    bool operator () (const Student &s1, const Student &s2){
        return s1.score > s2.score;
    }
};
priority_queue<Student, vector<Student>, cmp> pq;

6.4.5 priority_queue常见用途

  • 解决一些贪心问题
  • 对Dijkstra算法进行优化(优先队列本质是堆)。
  • 注意:使用top()前必须用empty()判断队列非空。

7 stack

后进先出的STL容器

7.1 定义

#include<stack>
using namespace std;
stack<int> name;

7.2 元素访问

栈只能通过top()函数访问栈顶元素。

printf("%d", name.top());

7.3 常用函数

  • name.push(x);将x入栈,复杂度O(1)
  • name.pop();栈顶元素出栈,复杂度O(1)
  • typename x = name.top();获得队首元素,复杂度O(1)
  • name.empty();
  • name.size()

7.4 stack常见用途

  • 解决递归问题

7.5 练习-括号匹配

#include<cstdio>
#include<iostream>
#include<stack>
#include<string>
using namespace std;

int main(){
	int n;
	scanf("%d",&n);
	getchar();
	string s;
	stack<char> st;
	while(n--){
		getline(cin,s);
		int len=s.size();
		bool flag=true;
		for(int i=0;i<len;i++){
			if(s[i]=='(' || s[i]=='[' || s[i]=='{'){
				st.push(s[i]);
			}
			else if(s[i]==')' || s[i]==']' || s[i]=='}'){
				if(st.empty()==true){
					flag=false;	//单独的后括号 
					break;
				}
				char c = st.top();	//pop()是void类型,需要用top()返回栈顶元素 
				st.pop();
				if(c=='['&&s[i]==']' || c=='('&&s[i]==')' || c=='{'&&s[i]=='}'){
					continue;
				}
				else{
					flag=false;	//不匹配 
					break;
				}
			}
		}
		if(flag==true&&st.empty()==true){
			printf("yes\n");
		}
		else{
			printf("no\n");
		}
		while(st.empty()!=true){
			st.pop();
		}
		
	}
	return 0;
}
/*
有一种更好的方法:
使用map创建char到int的对应关系,
通过两个括号对应的int相加是否等于某一定值,
判断两个括号是否匹配。
如:
map<char> list;
list中依次存储:{ 1, ( 2, [ 3, ] 4, ) 5, } 6
判断相加是否为7
*/

8 pair

可以看作一个内部有两个元素的结构体

8.1 定义

#include<utility>
using namespace std;
pair<typename1, typename2> name;

由于map内部实现涉及pair,因此添加map头文件后会自动添加utility头文件。

在定义时初始化,如:
pair<string, int> p("str", 100);

在代码中临时构建一个pair,有以下两种方法:

  • p = pair<string, int>("str", 100)
  • p = make_pair("str", 100)

8.2 元素访问

pair中只有两个元素,分别是first和second。按正常结构体的方式访问即可。

pair<string, int> p;
p.first = "this is a pair";
p.second =  10;

p = make_pair("this is a pair", 10);

p = pair<string, int>("this is a pair", 10);

cout<<p.first<<' '<<p.second<<endl;

8.3 常用函数

比较操作数。先比较first大小,如果相同再比较second大小。

p1 = pair<int, int>(1,3);
p2 = pair<int, int>(1,2);
if(p1 >= p2){}
else if(p1 < p2){}

8.4 pair常见用途

  • 用来代替二元结构体。不用自己写构造函数了。
  • 作为map的键值对进行插入、迭代等操作。如:
    #include<iostream>
    #include<string>
    #include<map>
    using namespace std;
    int main(){
    	map<string, int> mp;
    	mp.insert(make_pair("this is a pair", 100));
    	mp.insert(pair<string, int>("another pair", 200));
    	for(map<string, int>::iterator it = mp.begin(); it != mp.end(); it++){
    		cout << it->first << ' ' << it->second << endl;
    	}
    }
  • 注意,用迭代器操作是it->first,用实体操作是p.first。可以把迭代器想成指针。

9 algorithm头文件下的常用函数

需要添加using namespace std;

9.1 max()、min()、abs()

max(x, y)和min(x, y)

x和y可以是浮点数,但类型必须相同。

abs(x),x为整形。浮点数的绝对值使用cmath头文件下的fabs()函数。

9.2 swap()

swap(x, y),交换x和y的值。

9.3 reverse()

reverse(it1, it2)可以将数组指针在[it1, it2)之间的元素容器的迭代器在[it1, it2)范围内的元素进行反转。

例一:

int a[10] = { 1, 2, 3, 4, 5, 6 };
reverse(a, a+4);
for(int i=0;i<6;i++) printf("%d ",a[i]);

输出结果:

4 3 2 1 5 6

例二:

string str = "abcdefghi";
reverse(str.begin()+2, str.begin()+6);
printf("%s",str.c_str());

输出结果:

abfedcghi

9.4 next_permutation()

next_permutation()给出一个序列在全排列中的下一个序列。

int a[3] = {1, 2, 3};
do{
	printf("%d %d %d\n", a[0], a[1], a[2]);
}while(next_permutation(a, a+3));

输出结果:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

9.5 fill()

fill()可以把数组或容器中的某一段区间赋某个相同的值。和memset不同,这里的赋值可以是数组类型对应范围中的任意值。

参数写法依然是左闭右开。

int a[10] = { 1, 2, 3, 4, 5, 6 };
fill(a, a+4, 100);
for(int i=0;i<6;i++) printf("%d ",a[i]);

输出结果:

100 100 100 100 5 6

9.6 sort()

sort(首元素地址, 尾元素的下一个地址, 比较函数(缺省则默认递增序))

sort()用的比较多,直接贴一下结构体排序的代码吧。

PAT_A1025

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct grade{
	char num[20];
	int score;
	int final_rank;
	int location_number;
	int local_rank;
	grade(){
	}
	grade(char _num[],int _score){
		strcpy(num,_num);
		score=_score;
	}
};
bool cmp(grade g1,grade g2){
	if(g1.score!=g2.score) return g1.score>g2.score;
	else return strcmp(g1.num,g2.num)<0;
}
int main(){
	int n,m;
	int i,j;
	grade stu[30010];
	char num[20];
	int score;
	int count=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&m);
		int temp=count;
		for(j=0;j<m;j++){
			scanf("%s%d",num,&score);
			stu[count]=grade(num,score);
			stu[count].location_number=i;
			count++;	
		}
		sort(stu+temp,stu+count,cmp);
		stu[0+temp].local_rank=1;
		for(j=1;j<m;j++){
			if(stu[j+temp].score<stu[j+temp-1].score){
				stu[j+temp].local_rank=j+1;
			}
			else{
				stu[j+temp].local_rank=stu[j+temp-1].local_rank;
			}
		}
	}
	
	sort(stu,stu+count,cmp);
	stu[0].final_rank=1;
	for(j=1;j<count;j++){
		if(stu[j].score<stu[j-1].score){
			stu[j].final_rank=j+1;
		}
		else{
			stu[j].final_rank=stu[j-1].final_rank;
		}
	}
	
	printf("%d\n",count);
	for(i=0;i<count;i++)
		printf("%s %d %d %d\n",stu[i].num,stu[i].final_rank,stu[i].location_number,stu[i].local_rank);
	return 0;
}

9.7 lower_bound()和upper_bound()

两个函数均用在有序数组或容器中。

lower_bound(first, last, val)用来寻找在数组或容器的[first, last)范围内第一个值大于等于val的元素的位置,返回指针或迭代器。

upper_bound(first, last, val)找的是第一个值大于val的元素的位置。

如果查不到则返回可以插入该元素的位置。

int a[10] = {1, 2, 3, 3, 3, 5, 5, 5, 5, 5};
// -1
int* lowerPos = lower_bound(a, a+10, -1);
int* upperPos = upper_bound(a, a+10, -1);
printf("%d %d\n", lowerPos - a, upperPos - a);
// 1
lowerPos = lower_bound(a, a+10, 1);
upperPos = upper_bound(a, a+10, 1);
printf("%d %d\n", lowerPos - a, upperPos - a);
// 3
lowerPos = lower_bound(a, a+10, 3);
upperPos = upper_bound(a, a+10, 3);
printf("%d %d\n", lowerPos - a, upperPos - a);
// 4
lowerPos = lower_bound(a, a+10, 4);
upperPos = upper_bound(a, a+10, 4);
printf("%d %d\n", lowerPos - a, upperPos - a);
// 6
lowerPos = lower_bound(a, a+10, 6);
upperPos = upper_bound(a, a+10, 6);
printf("%d %d\n", lowerPos - a, upperPos - a);

输出结果:

0 0
0 1
2 5
5 5
10 10