前言
因为前面已经在专栏写过基本的入门知识,这里就不再赘述了,主要是c和c++的基础知识,基本的输入输出,最基本的递归算法等,因此我从c++的STL入手。
STL是C++中的标准库,主要是模板和函数模板,使用这些函数可以省很多事。
STL简介
C++中的STL分为三类,算法(algorithm),容器(container),迭代器(iterator),一共十三个头文件:
<algorithm>
<deque>
<functional>
<iterator>
<vector>
<list>
<map>
<memory>
<numeric>
<queue>
<string>
<set>
<stack>
<utility>
其中,算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。
容器包括<vector>,<list>,<deque>,<string>,<set>,<map>,<stack>和<queue>。
迭代器有<utility>,<iterator>和<memory>。
下面详解这些头文件和使用方法。
vector常见用法及其详解
1.vector的定义
vecto可以翻译为向量,我们可以理解为可变长度的数组。
如果遇到普通的数组超内存的情况,使用vector会让问题的解决便捷很多,而且也常常用于以邻接表存储图(日后补充)。
在使用时,需要在头文件下加上
#include<vector>
using namespace std;
在定义时使用
vector<typename> name;
进行定义,typename可以自己定义,int,double,char…
2.访问vector内元素
访问可以有两种写法:
1.通过下标访问
这里无需赘述,跟普通的数组一样,例如vector数组vi可以通过vi[0]、vi[1]访问,但是注意这里的下标范围是0到vi.size-1。
2.利用迭代器访问
迭代器|(iterator)类似于指针,定义方法为:
vector <typename> :: iterator it;
得到迭代器it后可以用*it可以访问vector里的元素。
vector<int> vi;
for(int i=1;i<=5;i++)
{
vi.push_back(i);//push_back向vector末尾添加一个元素i;删除时用pop_back()删除尾元素。
}
//用it指向vi的首元素地址
vector<int>::iterator it = vi.begin();
for(int i=0;i<5;i++)
{
cout<<*(it+i)<<endl;
}
另外,这里还要提一下跟begin()类似的end(),end()不同于begin取首元素,end()取的是尾元素的下一个地址,不存储任何元素,也就是常见的左闭右开思维。
//注意vector的迭代器不支持it<vi.end()的写法,因此我们写循环时写it!=vi.end();
for(vector<int>::iterator it=vi.begin();it!=vi.end();it++)
{
cout<<*(it)<<endl;
}
3.补充自带函数:
自带函数size(),通过size()可以获得vector的元素个数,时间复杂度为O(1)。
自带函数clear(),通过clear()来清空vector元素,时间复杂度O(N)。
自带函数insert函数用来向vector内插入元素,使用方法是insert(it,x)在迭代器it位置插入一个元素x,复杂度为O(N)。
自带函数erase(),有两种用法删除单个元素或者某区间内所有元素。
- erase(it)删除it处的元素
- erase(first,last)删除从first到last的所有元素,包括first但不包括last。
2.set的常见用法详解
1.set定义
set翻译为集合,是一个内部自动有序且不含重复元素的容器。考试中常有需要去掉重复元素的情况,而且有可能因为某些元素较大或者不是int型而不能直接开散列表,这种情况下就可以用set来保留元素本身而不考虑它的个数,而且加入后会自动排序。
补充:存在一个名为multiset的集合,允许重复元素。
写法跟vector类似
#include<set>
using namespace std;
//定义方法
set<typename> name;
2.访问set内元素
不同于vector,set只能通过iterator访问。
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(1);
cout<<"set 的 size 值为 :"<<s.size()<<endl;
cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;
cout<<"set 中的第一个元素是 :"<<*s.begin()<<endl;
cout<<"set 中的最后一个元素是:"<<*s.end()<<endl;
s.clear();
if(s.empty())
{
cout<<"set 为空 !!!"<<endl;
}
cout<<"set 的 size 值为 :"<<s.size()<<endl;
cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;
for(set<int>::iterator it=s.begin();it!=st.end();it++)
{
cout<<*(it)<<endl;
//注意除了vector和string之外的容器都不支持*(it+i)的访问方式
}
return 0;
}
3.set常用自带函数
- insert(x)可以把x插入到set容器,自动递增排序,复杂度log(N)
- find(value)返回set中对应值为value的迭代器,复杂度也为log(N)
- erase()用法同vector
- size()返回set内元素个数
- clear()用于清空set中所有元素
3.string的常见用法详解
1.string的定义
定义如下;
#include<string>
using namespace std;
string str="abcdefg";
2.string中内容的访问
1.可直接像字符数组那样去访问string,输入和输出也可以用cin和cout,不再赘述。
如果想要用printf来输出string,可以用c_str()将string类型转换为字符数组输出。
#include<stdio.h>
#include<string>
using namespace std;
int main()
{
string str="abcde";
printf("%s\n",str.c_str());
return 0;
}
2.利用迭代器访问
定义如下
string :: iterator it;
string就可以直接用迭代器加减某个数字进行访问例如,str.begin()+2
3.常用自带的函数
- operator+=:这是string的加法,将两个string拼接起来。可以直接用加法如string str1=“abcde”;string str2=“fgh”;string str3=str1+str2;
- length()/size():返回string的长度
- insert():有多种写法,如insert(pos,string)在pos位置插入字符串string,insert(it,it2,it3)it为原字符串欲插入位置,it2和it3位待查字符串的收尾迭代器,将串[it2,it3)插入到it位置
- erase():删除单个元素/删除某个区间内的元素
- clear():用于情况string的数据
- substr(pos,len):返回从pos号位置开始,长度为len的子串
- string::npos是一个常数值为-1,但由于unsigned_int类型,可以认为是该类型的最大值,。用于作为find函数失配时的返回值,可认为是-1或者4294967295
- find():str.find(str2)当str2是子串时,返回其位置,否则返回string::npos;str.find(str2,pos)从str的pos号位置开始匹配str2,返回值与上面相同
- replace(pos,len,str2)把str从pos号位置开始,长度为len的子串替换为str2,replace(it1,it2,str2)把str的迭代器[it1,it2)范围的子串替换为str2。
4.map的常见用法详解
1.map的定义
map翻译为映射,map可以将任何基本类型(包括STL容器)映射到任何基本类型的容器(包括STL容器),例如string到int的映射等。这就类似于字典,给出某个键,就必然有某个值跟它对应。
使用方法:
#include<map>
using namespace std;
map<typename1,typename2> mp;
前一个类型是键,后一个类型是值。注意:如果要建立字符串到整型的映射,必须用string而不能用char的数组。
常用于:
- 建立字符(字符串)与整数之间的映射
- 判断大整数或者其他类型的数据是否存在,可以把map当做bool数组使用
- 字符串和字符串之间的映射
2.map容器内元素的访问
1.通过下标访问
例如,对于map<char,int>mp来说,可以直接用mp[‘c’]来访问c对应的整数。当然map键是唯一的。
2.利用迭代器访问
定义如下:
map<typename1,typename2>::iterator it;
map的迭代器it可以用it->first和it->second来访问值
3.map实用函数实例解析
- find(key),返回键位key的迭代器,复杂度为O(logN)
- erase()两种用法,删除单个元素或者某个区间内的元素,erase(it)删除it所指向的元素的迭代器,erase(key)key使用想要删除的映射的键,erase(first,last)删除[first,last)的元素。
- size()获得map元素的个数
- clear()情况map中的所有元素
5.queue的常见用法详解
1.queue定义
queue是队列,是一个先进先出的容器。
使用方法:
#include<queue>
using namespace std;
queue<typename> name;
用途:
用于实现广度优先搜索
注意:使用front和pop函数前使用empty判断队列是否为空
2.queue容器内元素的访问
因为队列是先进先出的,因此在STL中只能通过front()来访问队首元素,或者back()来访问队尾元素。
3.queue常用函数实例解析
- push(x)将x加入队首
- front()、back()返回队首和队尾元素
- pop()令队列最前面的元素出队
- empty()检查queue是否为空
- size()返回元素个数
6.priority_queue的常见用法详解
1.priority_queue的定义
priority_queue又叫做优先队列,是利用堆来实现的,队首元素永远是优先级最好的哪一个。是对普通队列的延伸。
定义:
#include<queue>
using namespace std;
priority_queue<typename>name;
用途
解决某些贪心问题,也可以对Dijkstra算法进行优化。
2.priority_queue元素的访问
优先队列不同于普通队列,只能通过top()访问队首元素
3.priority_queue常用函数
- push(x),令x入队
- top(),访问队首元素
- pop(),将队首元素删除
- empty(),判断队列是否为空
- size(),返回队列元素个数
4.priority_queue元素优先级设置
1.基本数据类型的优先级设置
基本数据类型为int,double,char等类型,优先队列一般对它们的优先级设置是数字达到优先级越高,因此队列的首元素一般是最大的那个,如果是char类型那么是字典序最大的。
priority_queue<int>q;//这是按照最大的元素在队首设置的
//如果想要让优先队列把最小的元素放在队首,需要进行如下定义
priority_queue<int,vectot<int>, greater<int>> q;
2.结构体的优先级设置
例如如下结构体:
struct fruit
{
string name;
int price;
};
//如果想要按照水果价格优先级高,那就需要重载小于号
//“<”,也就是改变小于号的定义
struct fruit
{
string name;
int price;
friend bool operator <(fruit f1,fruit f2)
{
return f1.price<f2.price;
}
}
7.stack的常见用法详解
1.stack定义
stack是栈,是一个先进后出的容器。
定义:
#include<stack>
using namesapce std;
stack<typename> name;
用法:
用来模拟实现某些递归,防止程序对栈内存的限制而导致程序出错,一般来说,程序的栈内存空间小,对于很多要求来说会导致溢出。
2.stack容器内元素的访问
stack是一种后进先出的容器,因此可以通过top()来访问栈顶元素。
3.stack常用函数解析
- push(x),将x入栈
- top()获得栈顶元素
- pop()删除栈顶元素
- empty()检测stack内容是否为空
- size()返回元素个数
8.pair常见用法详解
1.pair定义
pair可以将两个元素绑定在一起合成一个元素,可以等价为一个结构体。
定义方法:
#include<utility>
using namespace std;
//如果记不住头文件可以直接用map代替,map头文件中包含了pair的定义
pair<typename1,typename2> name;
//例如pair<string,int>p;
//定义pair初始化时可以用小括号例如:
//pair<string,int>p("abcde",5);
//也可以用p=make_pair("abcde",5);
常见用法:
1.代替二元结构体,节省代码
2.可以作为map的键值对进行插入,例如
map<string,int> mp;
mp.insert(make_pair("abce",5));
2.pair中元素的访问
pair中有两个元素,分别为first和second,按照正常的结构体访问就可以。
3.pair常用函数实例解析
1.比较操作数:pair可以直接用==,!=,<,>比较,规则是先比较first,再比较second。
9.algorithm下的常用函数
algorithm中包括了很多实用数学函数,例如求最大,最小等等
- max(x,y),min(x,y),abs(x)分别是返回xy中的最大值,最小值,x的绝对值
- swap(x,y)交换x,y的值
- reverse(it,it2)可以将在[it,it2)之间的元素进行反转
- next_permutation()给出一个序列在全排列中的下一个序列,这可能读起来有些麻烦。
例如n==3时,全排列为:
123
132
213
231
312
321
这样123的下一个序列就是132
可以用以下代码获得全排列:
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={1,2,3}
//a[0]~a[2]之间的徐磊需要求解next_permutation
do{
printf("%d%d%d\n",a[0],a[1],a[2]);
}while(next_permutation(a,a+3));
}
- fill(),把数组或者容器中某个区间赋值为某个相同的值。和memset不同,这里的复制可以是数组类型对应范围中人一直。
- sort(),用来排序的函数,类似于c语言的qsort但是qsort很繁琐,比起来,sort更好用。
sort(首元素地址,尾元素地址的下一个地址,比较函数)
例如对某个数组a[6]={9,4,2,1,5,7},可以用sort(a,a+6)进行从小到大排序,如果想改变比较方法cmp。
bool cmp(int a,int b)
{
return a>b;
//可以理解为当a>b时把a放在b前面
}
//比较时
sort(a,a+6,cmp);
此外对结构体也可以排序
struct node
{
int x,y;
}s[10];
bool cmp(node a, node b)
{
return a.x>b.x;
}
//对s[10]赋值后
sort(s,s+10,cmp);
//可以对结构体s进行排序
也可以对容器进行排序,例如vector,string,deque等,注意数据类型,首位地址即可。
- lower_bound(),upper_bound(),lower_bound(first,last,val)和upper_bound(first,last,val)分别用于寻找在数组或者容器中[first,last)第一个值大于等于或者大于val的元素,如果是数组则返回指针,容器返回迭代器