目录:
一、C++标准模板库(STL)
1、头文件模板
2、vector
3、set
4、string
5、map
6、queue
7、priority_queue
8、stack
9、pair
10、algorithm头文件下常用函数
11、链表
二、常用泛型算法
1、find
2、find_if
3、count
4、accumulate
5、find_first_of
6、fill
7、fill_n
8、sort
9、unique
10、count_if
11、find_if
一、C++标准模板库(STL)
1、头文件模板
/* 头文件 */
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <list>
#include <bitset>
#include <iostream>
using namespace std;
2、vector
2.1、vector:本意为向量,这里作为“变长数组”,即“长度根据需要而自动改变的数组”。
2.2、使用前提:添加vector头文件
#include <vector>
using namespace std;
2.3、定义
vector<typename> name;
vector<typename> Arrayname[arraySize];
//举例
vector<int> name;
vector<node> name;
vector<vector<int> > name;
vector<int> Arrayname[100];
2.4、访问(两种访问方式:下标和迭代器=指针)
2.4.1、通过下标访问:和访问普通数组一样
vi[3]
2.4.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西
vector<typename>::iterator it = vi.begin();//定义迭代器it
通过*it来访问vector里的元素
vi.begin()+3//迭代器=指针
*(vi.begin()+3)//和v[3]是等价的
Note:在常用STL容器中,只有在vector和string中,才允许使用vi.begin()+3这种迭代器加上整数的写法
2.4.3、遍历
#include<cstdio>
#include<vector>
using namespace std;
int main()
{
vector<int> vi;
for(int i = 0; i <= 5; i++)
{
vi.push_back(i);
}
vi.insert(vi.begin()+2,-1);//将-1插在vi[2]的位置
for(int i = 0; i < vi.size(); i++)
{
//下标方式
printf("%d ",vi[i]);
}
for(vector<int>::iterator it = vi.begin(); it != vi.end(); it++)
{
//迭代器方式
printf("%d ",*it);
}
return 0;
}
2.5、常用函数
函数 | 功能 |
begin() | 首元素地址 |
end() | 尾元素地址的下一个地址(美国人思维比较习惯左闭右开) |
size() | 元素个数 |
clear() | 清空元素 |
push_back(x) | 在尾部添加元素 |
pop_back() | 删除尾部元素 |
insert(it,x) | 向指定位置插入一个元素 |
erase(it) | 删除指定位置元素 |
erase(first,last) | 删除 [ first , last ) 内的所有元素,如果要删除全部元素,写法是vi.erase(vi.begin(),vi.end()),此时等价于clear() |
3、set
3.1、set:翻译为集合,是一个内部自动有序且不含重复元素的容器。
作用:自动去重并按升序排序
3.2、使用前提:添加set头文件
#include<set>
using namespace std;
3.3、定义
set<typename> name;
set<typename> Arrayname[arraySize];
3.4、访问(只能通过迭代器=指针访问)
set<typename>::iterator it;//定义迭代器it
由于除开vector和string之外的STL容器都不支持*(it+i)的访问方式,因此只能按如下方式枚举:
#include<cstdio>
#include<set>
using namespace std;
int main()
{
set<int> st;
st.insert(3);
st.insert(5);
st.insert(2);
st.insert(3);
for(set<int>::iterator it = st.begin(); it != st.emd(); it++)
{
printf("%d",*it);//输出结果:2 3 5
}
return 0;
}
3.5、常用函数
函数 | 功能 |
begin() | 首元素地址 |
end() | 尾元素地址的下一个地址(美国人思维比较习惯左闭右开) |
size() | 元素个数 |
clear() | 清空元素 |
insert(x) | 插入元素 |
find(x) | 查找元素,返回对应元素的迭代器(指针) |
erase(it) | 删除指定位置元素,可结合find函数使用。st.erase(st.find(x)); |
erase(x) | 删除元素x,等价于st.erase(st.find(x)); |
erase(first,last) | 删除 [ first , last ) 内的所有元素 |
4、string
4.1、string:string类型对字符串常用的需求功能进行了封装,使得操作起来更方便,且不易出错。
4.2、使用前提:添加string头文件
#include<string>
using namespace std;
4.3、定义
string str = "abcd";
4.4、输入输出
4.4.1、如果要读入和输出整个字符串,则只能用cin和cout:
#include<iostream>//cin和cout在iostream头文件中
#include<string>
using namespace std;
int main()
{
string str;
cin>>str;
cout<<str;
return 0;
}
4.4.2、用c_str()将string类型转换为字符数组进行输出
#include<iostream>//cin和cout在iostream头文件中
#include<string>
using namespace std;
int main()
{
string str = "abcd";
printf("%s\n",str.c_str());//将string型str使用c_str()变成字符数组
return 0;
}
4.5、访问(两种访问方式:下标和迭代器=指针)
4.5.1、通过下标访问:和访问字符数组一样
str[3]
4.5.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西
string::iterator it;//定义迭代器it
通过*it来访问string里的元素
str.begin()+3//迭代器=指针
*(str.begin()+3)//和str[3]是等价的
Note:在常用STL容器中,只有在vector和string中,才允许使用vi.begin()+3这种迭代器加上整数的写法
4.6、常用函数
函数 | 功能 |
begin() | 首元素地址 |
end() | 尾元素地址的下一个地址(美国人思维比较习惯左闭右开) |
clear() | 清空元素 |
size() | 元素个数 |
length() | 元素个数,等价于size() |
+ | 拼接字符串。例如:str3 = str1 + str2; 或str1 += str1; |
比较运算符 | 按字典序比较两字符串大小 |
insert(pos,string) | 在pos号位置插入字符串string |
insert(it,it2,it3) |
串[it2,it3)将被插在it的位置上 |
erase(it) | 删除指定位置元素 |
erase(first,last) | 删除 [ first , last ) 内的所有元素 |
erase(pos,length) | 从pos位置开始删除length位 |
substr(pos,length) | 从pos位置开始,截取长度为length的子串 |
str.find(str2) | 当str2是str的子串时,返回其在str中第一次出现的位置;如果str2不是str的子串,那么返回string::npos |
str.find(str2,pos) | 从str的pos号位开始匹配str2 |
string::npos | 一个常数,用以作为find函数失配时的返回值 |
str.replace(pos,len,str2) |
把str从pos号位开始、长度为len的子串替换为str2 |
str.replace(it1,it2,str2) | 把str的迭代器[it1,it2)范围的子串替换为str2 |
5、map
5.1、map:翻译为映射。map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。map会以键从小到大的顺序自动排序,这是由于map内部是使用红黑树实现的(set也是),在建立映射的过程中会自动实现从小到大的排序功能。
5.2、使用前提:添加map头文件
#include<map>
using namespace std;
5.3、定义
map<typename1,typename2> mp;
举例:
map<string,int> mp;//如果字符串作为key,必须使用string而不能char数组
map<set<int>,string> mp;
5.4、访问(两种访问方式:下标和迭代器=指针)
5.4.1、通过下标访问:和访问普通数组一样
mp[key]
5.4.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西
map<typename1,typename2>::iterator it;//定义迭代器it
通过it->first来访问键,通过it->second来访问值
#include<cstdio>
#include<map>
using namespace std;
int main()
{
map<char,int> mp;
mp['m'] = 20;
mp['r'] = 30;
mp['a'] = 40;
for(map<char,int>::iterator it = mp.begin(); it != mp.end(); it++)
{
printf("%c %d\n",it->first,it->second);
}
return 0;
}
5.5、常用函数
函数 | 功能 |
begin() | 首元素地址 |
end() | 尾元素地址的下一个地址(美国人思维比较习惯左闭右开) |
size() | 元素个数 |
clear() | 清空元素 |
find(key) |
查找key,返回迭代器 |
erase(it) | 删除指定位置元素 |
erase(key) | 删除key |
erase(first,last) | 删除 [ first , last ) 内的所有元素 |
6、queue
6.1、queue:翻译为队列,先进先出的容器
6.2、使用前提:添加queue头文件
#include<queue>
using namespace std;
6.3、定义
queue<typename> name;
6.4、访问
只能通过front()来访问队首元素,通过back()访问队尾元素
6.5、常用函数
函数 | 功能 |
size() | 元素个数 |
empty() | 检测是否为空,返回bool |
push(x) | 入队列 |
pop() | 出队列 |
front() | 获得队首元素 |
back() | 获得队尾元素 |
Note:使用front()和pop()函数时,必须用empty()判断队列是否为空,否则可能因为队空而出现错误。
7、priority_queue
7.1、priority_queue:翻译为优先队列,其底层是用堆来进行实现的,队首元素一定是当前队列中优先级最高的那一个。
7.2、使用前提:添加queue头文件
#include<queue>
using namespace std;
7.3、定义(设置优先级)
如何定义优先队列内元素的优先级是运用好优先队列的关键
priority_queue<int> name;//默认大的优先级越大,等价于priority_queue<int,vector<int>,less<int> > q;
//基本数据类型的优先级设置
//第二个参数是来承载底层数据结构堆的容器,第三个参数是对第一个参数的比较类
priority_queue<int,vector<int>,less<int> > q;//less表示大的优先级越大
priority_queue<int,vector<int>,greater<int> > q;//greater表示小的优先级越大
//结构体的优先级设置
struct fruit{
string name;
int price;
};
struct cmp{
//优先队列的这个函数与sort中的cmp函数的效果是相反的
bool operator () (fruit f1,fruit f2)
{
return f1.price > f2.price;
}
};
priority_queue<fruit,vector<fruit>,cmp> q;
7.4、访问
只能通过top()来访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。
7.5、常用函数
函数 | 功能 |
size() | 元素个数 |
empty() | 检测是否为空,返回bool |
push(x) | 入队列 |
pop() | 出队列 |
top() | 获得队首元素 |
Note:使用top()函数时,必须用empty()判断优先队列是否为空,否则可能因为队空而出现错误。
8、stack
8.1、stack:翻译为栈,后进先出的容器。
用来模拟实现一些递归,防止程序对栈内存的限制而导致程序运行出错。
8.2、使用前提:添加stack头文件
#include<stack>
using namespace std;
8.3、定义
stack<typename> name;
8.4、访问
只能通过top()来访问栈顶元素
8.5、常用函数
函数 | 功能 |
size() | 元素个数 |
empty() | 检测是否为空,返回bool |
push(x) | 入栈 |
top() | 获得栈顶元素 |
pop() | 弹出栈顶元素 |
9、pair
9.1、pair:实际上可以看作一个内部有两个元素的结构体
常见用途一:代替二元结构体,节省编码时间
常见用途二:作为map的键值对来进行插入
#include<string>
#include<map>
int main()
{
map<string,int> mp;
mp.insert(make_pair("hehe",5));
mp.insert(pair<string,int>("hehe",5));
for(map<string,int>::iterator it = mp.begin(); it != mp.end(); it++)
{
printf("%c %d\n",it->first,it->second);
}
}
9.2、使用前提:添加utility头文件或map头文件
map内部实现涉及pair,因此添加map头文件时会自动添加utility头文件
#include<map>
//或 #include<utility>
using namespace std;
9.3、定义
//定义
pair<typename1,typename2> name;
//定义并初始化
pair<string,int> p("haha",5);
//临时变量(只用一次的)
pair<string,int>("haha",5)
或
make_pair("haha",5)
9.4、访问
pair中只有两个元素,分别是first和second,只需要按正常结构体的方式去访问即可。
9.5、常用函数
函数 | 功能 |
比较运算符 | 比较规则是先以first的大小作为标准,相等时才去判别second的大小 |
10、algorithm头文件下常用函数
函数 | 功能 |
max(x,y) | 最大值 ( int和double均可) |
max(x,max(y,z)) | 三个数的最大值 |
min(x,y) | 最小值 |
abs(int x) | 绝对值( Note:浮点型的绝对值请用math里的fabs) |
swap(x,y) | 交换 |
reverse(it,it2) | 将数组指针或容器迭代器在[it,it2)范围内的元素反转 |
next_permutation(it,it2) |
给出一个序列在全排列中的下一个序列 |
fill(it,it2,value) | 把数组或容器某区间赋为某个相同的值 |
sort(it,it2) | 排序 |
sort(it,it2,cmp) | 排序 |
lower_bound(it,it2,val) |
寻找数组或容器的[it,it2)范围内第一个值大于等于val的元素的位置,返回指针或迭代器,如果不存在,则返回可插入该元素的指针或迭代器 |
upper_bound(it,it2,val) | 寻找数组或容器的[it,it2)范围内第一个值大于val的元素的位置,返回指针或迭代器,如果不存在,则返回可插入该元素的指针或迭代器 |
11、链表
11.1、链表结点
struct node{
typename data;//数据域
node* next;//指针域
}
11.2、为链表结点分配内存空间和释放内存空间
node* p = new node;//分配内存空间
delete(p);//释放内存空间
Note:new运算符和delete运算符必须成对出现,否则会容易产生内存泄露,从编程习惯上,应养成即时释放空间的习惯。不过一般考试中,分配的空间在程序结束时即被释放,不会有太大影响,以下代码没有释放空间。
11.3、链表基本操作
11.3.1、创建链表
#include<stdio.h>
#include<stdlib.h>
struct node{
int data;
node* next;
}
node* create(int Array[],int len)
{
node *p,*pre,*head;
head = new node;
head->next = NULL;
pre = head;
for(int i = 0; i < len; i++)
{
p = new node;
p->data = Array[i];
p->next = NULL;
pre->next = p;
pre = p;
}
return head;
}
int main()
{
int Array[5] = {5,3,6,1,2};
node* L = create(Array,5);
L = L->next;
while(L != NULL)
{
printf("%d",L->data);
L = L->next;
}
}
11.3.2、查找元素
int search(node* head,int x)
{
int count= 0;
node* p = head->next;
while(p != NULL)
{
if(p->data == x)
{
count++;
}
p = p->next;
}
return count;
}
11.3.3、插入元素
void insert(node* head,int pos,int x)
{
node* p = head;
for(int i = 0; i < pos -1; i++)
{
p = p->next;
}
node* q = new node;
q->data = x;
q->next = p->next;
p->next = q;
}
11.3.4、删除元素
//删除以head为头结点的链表中所有数据域为x的结点
void del(node* head,int x)
{
node* p = head->next;
node* pre = head;
while(p != NULL)
{
if(p->data == x)
{
pre->next = p->next;
delete(p);
p = pre->next;
}
else
{
pre = p;
p = p->next;
}
}
}
11.4、静态链表
struct Node{
typename data;
int next;//令数组的下标直接表示结点的地址,实现原理是hash
}node[size];
二、常用泛型算法
1、find
vector<int> a;
int val = 10
//find接受两个迭代器和要查找的值,如果找到,则返回该值对应的迭代器
//否则返回a.end()
auto it = find(a.begin(), a.end(), val);
2、find_if
vector<int> a;
int val = 10
//find_if 返回第一个满足条件的迭代器
//否则返回a.end()
auto it = find(a.begin(), a.end(), [](int a)->bool { return a == 1; });
3、count
vector<int> a;
int val = 10
//count 与find 的参数类型相同,统计两个迭代器之间的值val出现的次数
int s = count(a.begin(), a.end(), val);
4、accumulate
accumulate 带有三个形参。
头两个形参指定要累加的元素范围。第三个形参则是累加的初值。accumulate 函
数将它的一个内部变量设置为指定的初值,然后在此初值上累加输入范围 accumulate
用于指定累加起始值的第三个实参是必要的,因
为 accumulate 对将要累加的元素类型一无所知,因此,除此
之外,没有别的办法创建合适的起始值或者关联的类型。
accumulate 对要累加的元素类型一无所知,这个事实有两层含义。首先,调用
该函数时必须传递一个起始值,否则,accumulate 将不知道使用什么起始值。
其次,容器内的元素类型必须与第三个实参的类型匹配,或者可转换为第三个实
参的类型。在 accumulate 内部,第三个实参用作累加的起点;容器内的元素按
顺序连续累加到总和之中。因此,必须能够将元素类型加到总和类型上。
int main()
{
vector<int> vec;
for(int i = 0; i < 10; i++)
{
vec.push_back(i);
}
//在100000的基础上进行累加
int sum = accumulate(vec.begin(),vec.end(),100000);
cout << sum << endl;
return 0;
}
还可以进行字符串的累加
int main()
{
string vec = "qweqwe";
//可以进行字符串的拼接
string sum = accumulate(vec.begin(),vec.end(),string("pppppp"));
cout << sum << endl;
return 0;
}
5、find_first_of
除了 find 之外,标准库还定义了其他一些更复杂的查找算法。当中的一部
分类似 string 类的 find 操作,其中一个是 find_first_of 函数。这个算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。
int main()
{
srand(time(0));
vector<int> vec1;
vector<int> vec2;
for(int i = 0; i < 1000; i++)
{
vec1.push_back(rand() % 100);
vec2.push_back(rand() % 100);
}
auto it = find_first_of(vec1.begin(),vec1.end(),vec2.begin(),vec2.end());
if(it != vec1.end())
{
cout << *it << endl;
}
}
6、fill
fill 带有一对迭代器形参,用于指定要写入的范围,而所写的值是它的第三个
形参的副本。执行时,将该范围内的每个元素都设为给定的值。如果输入范围有
效,则可安全写入。这个算法只会对输入范围内已存在的元素进行写入操作
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 10; i++)
{
vec1.push_back(rand() % 100);
}
for(auto val:vec1)
{
cout << val << " ";
}
cout << endl;
fill(vec1.begin(),vec1.end(),0);
for(auto val:vec1)
{
cout << val << " ";
}
cout << endl;
}
7、fill_n
fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函
数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数
假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素
的空容器上调用 fill_n 函数;
这个 fill_n 函数的调用将带来灾难性的后果。我们指定要写入 10 个元
素,但这些元素却不存在——vec 是空的。其结果未定义,很可能导致严重的运行时错误。 对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 10; i++)
{
vec1.push_back(rand() % 100);
}
for(auto val:vec1)
{
cout << val << " ";
}
cout << endl;
//fill使用
fill_n(vec1.begin(),5,0);
for(auto val:vec1)
{
cout << val << " ";
}
cout << endl;
}
8、sort
sort 内部采用快排算法。
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 100; i++)
{
vec1.push_back(rand() % 100);
}
sort(vec1.begin(),vec1.end());
for(auto val:vec1)
{
cout << val << " ";
}
cout << endl;
}
9、unique
去除重复数据
如果要删除重复的项,必须使用容器操作,在本例中调用 erase 实现该功
能。这个函数调用从 it 指向的元素开始删除,直到 words 的最后一
个元素也删除掉为止。调用之后,words 存储输入的 8 个不相同的元素。
算法不直接修改容器的大小。如果需要添加或删除元素,则必
须使用容器操作。
值得注意的是,对没有重复元素的 vector 对象,调用 erase 也是安全的。
如果不存在重复的元素,unique 就会返回 words.end(),此时,调用 erase 的
两个实参值相同,都是 words.end()。两个迭代器相等这个事实意味着 erase 函
数要删除的范围是空的。删除一段空的范围没有任何作用,所以即使输入中没有
重复的元素,我们的程序仍然正确。
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 100; i++)
{
vec1.push_back(rand() % 100);
}
sort(vec1.begin(),vec1.end());
//去除重复
auto it = unique(vec1.begin(),vec1.end());
vec1.erase(it,vec1.end());
}
10、count_if
按条件统计满足条件的元素的个数
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 100; i++)
{
vec1.push_back(i);
}
sort(vec1.begin(),vec1.end());
//vector<int>::size_type vectmp = count_if(vec1.begin(),vec1.end(),comp());
int a = count_if(vec1.begin(),vec1.end(),comp());
//cout << vectmp << endl;
cout << a << endl;
return 0;
}
11、find_if
标准库定义了一个 find_if 函数。与 find 一样,find_if 函数带有一对迭代器形参,指定其操作的范围。与 count_if 一样,该函数还带有第三个形参,表明用于检查范围内每个元素的谓词函数。find_if 返回一个迭代器,指向第一个谓词函数返回非零值的元素。如果
这样的元素不存在,则返回第二个迭代器实参。使用 find_if 函数重写上述例题中统计长度大于 6 的单词个数的程序部分。
class comp
{
public:
bool operator()(int a)
{
return a > 6;
}
};
int main()
{
srand(time(0));
vector<int> vec1;
for(int i = 0; i < 100; i++)
{
vec1.push_back(rand() % 100);
}
sort(vec1.begin(),vec1.end());
auto it = find_if(vec1.begin(),vec1.end(),comp());
cout << *it << endl;
return 0;
}