总结于《算法笔记》——胡凡、曾磊
本博客使用的IDE为Dev C++,若要使用C++11的新特性,需在工具>编辑选项里勾上“编译时加入以下指令”,并在下面框框里复制粘贴上
-std=c++11
。
vector(向量),是长度可以根据需要改变的数组。
使用时要加上#include
,然后在下一行加上using namespace std;
vector
//例:
vector name;
vector name;
vector name;
//注意:如果 typename也是一个STL容器,定义的时候要记得在>>符号之间加上空格,因为有的编译器可能会把它视为移位操作,导致编译错误。如下面定义二维数组。
vector数组:
vector
vector v[100]; //一维长度已经固定为100
vector > name; //两维的长度都可变
有两种方法:
vector[index]
迭代器(iterator)可以理解为指针。
vector
//例:
#include
#include
using namespace std;
int main(){
vector v;
for(int i = 1; i <= 5; i++) {
v.push_back(i); //push_back(i)在v的末尾添加元素i
}
//v.begin()为取v的首元素地址,而it指向这个地址
vector::iterator it = v.begin();
for(int i = 0; i < 5; i++) {
printf("%d", *(it + i)); //v[i]等同于*(v.begin() + i)
}
return 0;
}
此外,迭代器还可进行自加操作:++it
和it++
:
#include
#include
using namespace std;
int main(){
vector v;
for(int i = 1; i <= 5; i++) {
v.push_back(i);
}
// v.begin()是取v的首地址,而v.end()取v尾元素地址的下一个地址
// vector的迭代器不支持it < vi,end()写法,因此循环条件只能用it != vi.end()
for(vector::iterator it = v.begin(); it != v.end(); it++) {
printf("%d", *it);
}
return 0;
}
最后需要指出,在常用STL容器中,只有在vector和 string中,才允许使用 v.begin() + 3
这种迭代器加上整数的写法。
push_back(x)
就是在vector后加一个元素x。
pop_back()
用以删除vector的尾元素
size()
用来获得vector中元素个数。
size返回的是 unsigned类型,不过一般来说用%d不会出很大问题,这一点对所有STL容器都是一样的。
clear()
用于清空vector中的所有元素。
insert(it, x)
用来向 vector的任意迭代器it处插入一个元素x。
erase()
有两种用法:
删除单个元素:
erase(i)
即删除迭代器为it处的元素。
删除一个区间内所有的元素:
erase(first, last)
即删除[first, last)内的所有元素。
在一些元素不确定的场合可以使用。
set翻译为集合,是一个内部自动有序且不含重复元素的容器。
使用时要加上#include
,然后在下一行加上using namespace std;
set
set数组:
set
set只能通过迭代器访问:
set
由于除开vector和string之外的STL容器都不支持*(it + i)的访问方式,因此只能按如下方式枚举:
#include
#include
using namespace std;
int main(){
set st;
for(int i = 1; i <= 5; i++) {
st.insert(i); // insert(i)将i插入set中
}
for(set::iterator it = st.begin(); it != st.end(); it++) {
printf("%d", *it);
}
return 0;
}
insertx(x)
和将x插入set容器中,并自动递增排序和去重。
find(value)
返回set中对应值为value的迭代器。
#include
#include
using namespace std;
int main(){
set st;
for(int i = 1; i <= 5; i++) {
st.insert(i);
}
set::iterator it = st.find(2);
printf("%d\n", *it);
//也可以写成
//printf("%d\n", *(st.find(2)));
return 0;
}
size()
用来获得set中元素个数。
clear()
用于清空set中的所有元素。
erase()有两种用法:
删除单个元素:
有两种方法:
st.erase(it)
,it为所需要删除元素的迭代器,可结合find()函数使用。
#include
#include
using namespace std;
int main(){
set st;
for(int i = 1; i <= 5; i++) {
st.insert(i);
}
st.erase(st.find(1));
st.erase(st.find(3));
for(set::iterator it = st.begin(); it != st.end(); it++) {
printf("%d", *it);
}
return 0;
}
输出结果:
245
st.erase(value)
,value为所需要删除的元素。
#include
#include
using namespace std;
int main(){
set st;
for(int i = 1; i <= 5; i++) {
st.insert(i);
}
st.erase(4);
for(set::iterator it = st.begin(); it != st.end(); it++) {
printf("%d", *it);
}
return 0;
}
输出结果:
1235
删除一个区间内所有的元素:
erase(first, last)
即删除[first, last)内的所有元素。
#include
#include
using namespace std;
int main(){
set st;
for(int i = 1; i <= 5; i++) {
st.insert(i);
}
set::iterator it = st.find(4);
st.erase(it, st.end()); //删除元素4至set末尾之间的元素,即4和5
for(it = st.begin(); it != st.end(); it++) {
printf("%d", *it);
}
return 0;
}
输出结果:
123
遇到需要去重但不方便直接开数组的情况,可尝试用set解决。
使用时要加上#include
(注意string.h和string不是一样的头文件),然后在下一行加上using namespace std;
string str;
初始化:
string str = "abcd";
str[index]
如果要读入和输出整个字符串,则只能用cin和cout。
string不想其他STL容器那样需要参数,因此可以直接如下定义:
string::iterator it;
string和vector一样,支持直接对迭代器进行加减,如str.begin() + 3
。
string的加法,可以将两个string直接拼接起来。
#include
#include
using namespace std;
int main(){
string str1 = "abc", str2 = "xyz", str3;
str3 = str1 + str2;
str1 += str2;
cout << str1 << endl;
cout << str3 << endl;
return 0;
}
两个string类型可直接用==、!=、< 、<=、>、>=比较大小,比较规则是字典序(即"aac"小于"aba")。
size()
和length()
基本相同
insert(pos, string),在pos号位置插入string(从0开始)。
#include
#include
using namespace std;
int main(){
string str1 = "abcxyz", str2 = "opq";
str1.insert(3, str2);
cout << str1 << endl;
return 0;
}
输出结果:
abcopqxyz
insert(it, it2, it3),it为原字符串的欲插入位置,it2和it3为待插字符串的首尾迭代器,表示[it2, it3)将被插在it的位置上。
#include
#include
using namespace std;
int main(){
string str1 = " Hi, man!", str2 = "See you.";
str1.insert(str1.begin(), str2.begin(), str2.end());
cout << str1 << endl;
return 0;
}
输出结果:
See you. Hi, man!
删除单个元素:
erase(i)
即删除迭代器为it处的元素。
删除一个区间内所有的元素:
str.erase(first, last)
,其中first为区间的起始迭代器,而last为需要删除的区间的末尾迭代器的下一个地址。str.erase(pos, length)
,其中pos为需要开始删除的起始位置,而length为删除的字符个数。clear()
用于清空string中的所有元素。
substr(pos, len)
返回从pos号开始、长度为len的子串。
#include
#include
using namespace std;
int main(){
string str = "abcxyz";
cout << str.substr(1, 3) << endl;
return 0;
}
输出结果:
bcx
string::npos是一个常数,其本身的值为-1,是unsighed_int类型。string::npos用以作为find()函数失配时的返回值。
str.find(str2)
,当str2是str的子串时,返回其在str中第一次出现的位置;如果str2不是str的子串,则返回string::npos。
str.find(str2, pos)
,从str的pos号开始匹配str2,返回值与上相同。
#include
#include
using namespace std;
int main(){
string str = "Thank you for your help.";
string str1 = "you";
string str2 = "me";
if(str.find(str1) != string::npos)
cout << str.find(str1) << endl;
if(str.find(str1, 7) != string::npos)
cout << str.find(str1, 7) << endl;
if(str.find(str2) != string::npos)
cout << str.find(str2) << endl;
else
cout << "There is no position for me." << endl;
return 0;
}
输出结果:
6
14
There is no position for me.
str.replace(pos, len, str2)
把str从pos位开始、长度为len的字符串换成str2.
str.replace(it1, it2, str2)
把str的迭代器[it1, it2)范围的子串换成str2.
#include
#include
using namespace std;
int main(){
string str = "Thank you for your help.";
string str1 = "HAPPY";
string str2 = "BITE";
cout << str.replace(10, 2, str1) << endl;
cout << str.replace(str.begin(), str.begin() + 1, str2) << endl;
return 0;
}
输出结果:
Thank you HAPPYr your help.
BITEhank you HAPPYr your help.
map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。
使用时要加上#include
,然后在下一行加上using namespace std;
map
如果是字符串到整型的映射,必须使用string而不能用char数组:
map
map的键和值也可以是STL容器:
map
map中的键是唯一的。
mp['key']
map
代码:
#include
#include
输出结果:
b 2
删除单个元素:
mp.erase(it)
,it为欲删除的元素的迭代器。
mp.erase(key)
,key为欲删除的映射的键。
#include
#include
输出结果:
a 1
d 4
删除一个区间内所有的元素:
mp.erase(first, last)
,删除[first, last)内的所有元素,first为起始迭代器,last为末尾迭代器的下一个地址。
#include
#include
输出结果:
a 1
需要建立字符(或字符串)与整数之间映射的题目;
判断大整数或其他类型数据是否存在的题目,可以把map当bool数组用;
字符串和字符串的映射。
queue(队列)是STL中实现的一个先进先出的容器。
使用时要加上#include
,然后在下一行加上using namespace std;
queue
因为队列是一种先进先出的数据结构,因此在SLT中只能用front()
访问队首元素,通过back()
访问队尾元素。
#include
#include
using namespace std;
int main(){
queue q;
for(int i = 1; i <= 5; i++) {
q.push(i); //push(i)用以将i压入队列
}
printf("%d %d\n", q.front(), q.back());
return 0;
}
输出结果:
1 5
push(x)
将x压入队列。
front()
用于访问队首元素,back()
用于访问队尾元素。
pop()
令队首元素出队。
#include
#include
using namespace std;
int main(){
queue q;
for(int i = 1; i <= 5; i++) {
q.push(i); // push(i)用以将i压入队列
}
for(int i = 1; i <= 5; i++) {
printf("%d ", q.front()); // 输出队首元素
q.pop(); //出队首元素3次(依次为1 2 3)
}
printf("\n");
printf("%d\n", q.front());
return 0;
}
输出结果:
1 2 3 4 5
0
empty()
检测 queue是否为空,返回true则空,返回false则非空。
#include
#include
using namespace std;
void isEmpty(queue q){ // 判断队列是否为空的函数
if(q.empty() == true){
printf("Empty\n");
}else{
printf("Not empty\n");
}
}
int main(){
queue q;
isEmpty(q); // 一开始队列里没有元素,所以是空
q.push(1);
isEmpty(q); // 在入队"1"后,队列非空
return 0;
}
输出结果:
Empty
Not empty
当需要实现广度优先搜索时,可以不用自己手动实现一个队列,而是用queue作为代替,以提高程序的准确性。
front()
和pop()
函数前,必须用empty()
判断队列是否为空。priority_queue又叫优先队列,其底层是用堆进行实现的。队首元素一定是当前队列中优先级最高的那一个。
可以在任何时间往优先队列里加入(push)元素,优先队列底层的堆(heap)会随时调整结构,使得每次的队首元素都是优先最大的。
关于这里的优先级则是规定出来的。
使用时要加上#include
,然后在下一行加上using namespace std;
priority_queue
和queue不同,priority_queue没有front()
和back()
函数,只能通过top()
函数来访问队首元素(堆顶元素),也就是优先级最高的元素。
#include
#include
using namespace std;
int main(){
priority_queue q;
q.push(4);
q.push(5);
q.push(1);
printf("%d", q.top());
return 0;
}
输出结果:
4
获得队首元素(即堆顶元素)出队。
#include
#include
using namespace std;
int main(void){
priority_queue q;
q.push(3);
q.push(4);
q.push(1);
printf("%d\n", q.top());
q.pop();
printf("%d\n", q.top());
return 0;
}
输出结果:
4
3
此处指的基本数据类型就是int型、 double型、char型等可以直接使用的数据类型,优先队列对他们的优先级设置一般是数字大的优先级越高。
下面两种定义方式是等价的(注意最后两个>之间有一个空格):
priority_queue
priority_queue
第二种定义方法尖括号内多了两个参数:
vector
填写的是来承载底层数据结构堆(heap)的容器。如果第一个参数是double型或char型,则此处只需填写vector
或vector
;
less
是对第一个参数的比较类。less
表示数字大的优先级越大,而greater
表示数字小的优先级越大。
示例:
#include
#include
using namespace std;
int main(void){
priority_queue, greater > q; //让优先队列总是把最小的元素放队首
q.push(3);
q.push(4);
q.push(1);
printf("%d\n", q.top());
return 0;
}
输出结果:
1
通过改变小于号"<"的功能,即把它重载(overload)为大于号的功能。
现在希望按水果的价格高的为优先级高,需要重载小于号:
struct fruit {
string name;
int price;
friend bool operator < (fruit f1, fruit f2) { // friend为友元,bool operator < (fruit f1, fruit f2)对fruit类型的操作符"<"进行重载
return f1.price < f2.price; // 重载后小于号还是小于号的作用。
}
};
此时可以直接定义fruit类型的优先队列,其内部就是以价格高的水果为优先级高。priority_queue
同理,希如果望按水果的价格低的为优先级高,只需要把return中的大于号改为小于号。
示例:
#include
#include
#include
using namespace std;
struct fruit {
string name;
int price;
friend bool operator < (fruit f1, fruit f2) {
return f1.price > f2.price; //优先最小的
}
}f1, f2, f3;
int main(void){
priority_queue q;
f1.name = "桃子";
f1.price = 3;
f2.name = "苹果";
f2.price = 4;
f3.name = "香蕉";
f3.price = 1;
q.push(f1);
q.push(f2);
q.push(f3);
cout << q.top().name << " " << q.top().price << endl;
return 0;
}
输出结果:
香蕉 1
记住:**优先队列的这个函数与sort中的函数效果是相反的!**在排序中,return f1.price > f2.price;
表示按价格从高到低,而在优先队列中却是把价格低的放队首。这是因为优先队列本身默认的规则就是优先级高的放队首,因此把小于号重载为大鱼号的功能时只是把这个规则反了一下。
可以把sort中的cmp函数卸载结构体外面:
示例:
#include
#include
#include
using namespace std;
struct fruit {
string name;
int price;
}f1, f2, f3;
struct cmp {
bool operator () (fruit f1, fruit f2) {
return f1.price > f2.price; //优先最小的
}
};
int main(void){
priority_queue, cmp> q; //这种情况只能这样定义优先队列
f1.name = "桃子";
f1.price = 3;
f2.name = "苹果";
f2.price = 4;
f3.name = "香蕉";
f3.price = 1;
q.push(f1);
q.push(f2);
q.push(f3);
cout << q.top().name << " " << q.top().price << endl;
return 0;
}
输出结果:
香蕉 1
如果结构体内的数据庞大(例如出现字符串或数组),建议使用引用来提升效率,此时比较类的参数中要加入const
和&
,如下所示:
friend bool operator () (const fruit &f1, const fruit &f2) {
return f1.price > f2.price;
}
bool operator () (const fruit &f1, const fruit &f2) {
return f1.price > f2.price;
}
priority_queue可以解决一些贪心问题,也可以对DijKstra算法进行优化(因为优先队列的本质是堆)。
注意:使用top()
函数前,要用empty()
判断优先队列是否为空。
栈,是STL实现的一个后进后出的容器。
使用时要加上#include
,然后在下一行加上using namespace std;
stack
由于栈本身就是一种后出先出的数据结构,在STL中的stack中只能通过top()来访问栈顶元素。
#include
#include
using namespace std;
int main(void){
stack st;
for(int i = 1; i <= 5; i++) {
st.push(i);
}
printf("%d\n",st.top());
return 0;
}
输出结果:
5
弹出栈顶元素。
#include
#include
using namespace std;
int main(void){
stack st;
for(int i = 1; i <= 5; i++) {
st.push(i);
}
for(int i = 1; i <= 3; i++) {
st.pop(); // 连续3次将栈顶元素出栈,即5, 4, 3出栈
}
printf("%d\n",st.top());
return 0;
}
输出结果:
2
stack用来模拟一些递归,防止程序对栈内存的限制而导致程序出错。
pair可以看作内部有两个元素的结构体,且这两个元素的类型是可以指定的,如:
struct pair {
typename1 first;
typename2 second;
};
使用时要加上#include
,然后在下一行加上using namespace std;
。注意:由于map的内部实现中涉及pair,因此添加map头文件时会自动添加utility头文件。因此可偷懒只添加#include
。
有两个参数,分别对应first和second的数据类型,他们可以是任意基本数据类型或容器。
pair
如:
pair
如果想在定义时初始化:
pair
如果想在代码中临时构建一个pair,有如下两种方法:
pair("haha", 5);
make_pair("haha", 5);
pair中只有两个元素,分别是first和second,只需按正常结构体的方式访问即可。
#include
#include
#include
using namespace std;
int main(void){
pair p;
p.first = "haha";
p.second = 5;
cout << p.first << " " << p.second << endl;
p = make_pair("xixi", 3); //自带函数
cout << p.first << " " << p.second << endl;
p = pair("wuwu", 555);
cout << p.first << " " << p.second << endl;
return 0;
}
输出结果:
haha 5
xixi 3
wuwu 555
两个pair类型数据可直接用==、!=、<等比较大小,规则是先比first,first相等时再比second。
#include
#include
#include
using namespace std;
int main(void){
pair p1(5, 55);
pair p2(5, 555);
pair p3(10, 5);
if(p1 < p3) cout << "p1 < p3" << endl;
if(p1 <= p3) cout << "p1 <= p3" << endl;
if(p1 < p2) cout << "p1 < p2" << endl;
return 0;
}
输出结果:
p1 < p3
p1 <= p3
p1 < p2
用来代替二元结构体及其构造函数,可以节省编码时间。
作为map的键值对来进行插入。
#include
#include
输出结果:
haha 2
heihei 5