C/C++基础与STL
- 头文件
C++常用头文件如下,无论是否用到,都可以在代码开头复制写上下面所有的头文件。
#include<cstdio> // 包括了printf和scanf
#include<iostream> // 包括了cin和cout和endl
#include<iomanip> // 包括了fixed和setprecision
#include<cmath> // 包括了数学函数
#include<cstring> // 包括了字符串函数
#include<algorithm> // 包括了STL中的各种算法
#include<string> // 包括了string类
#include<queue> // 包括了队列
#include<stack> // 包括了栈
#include<map> // 包括了映射
#include<unordered_map> // 包括了无序映射
#include<set> // 包括了集合
#include<unordered_set> // 包括了无序集合
#include<vector> // 包括了可变数组
#include<bitset> // 包括了位集合
#include<deque> // 包括了双端队列
#include<cctype> // 包括了字符处理
当然也可以使用万能头文件<bits/stdc++.h>,万能头文件相当于包括了以上所有文件,但有些编译器并不支持此头文件。
- cout的输出精度
无论是float还是double类型的小数,直接使用cout输出默认只输出6位有效数字,可以使用头文件<iomanip>中的setprecision来设置精度输出几位有效数字。无论是直接输出还是设置精度再输出,其结果都是四舍五入后再输出。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double x=1234.56789;
cout<<x<<endl; //输出结果为1234.57
cout<<setprecision(6)<<x<<endl; //输出结果为1234.57
cout<<setprecision(9)<<x<<endl; //输出结果为1234.56789
}
输出结果为:1234.57,1234.57,1234.56789
当setprecision设置的精度太小无法表示完整数部分,输出结果会用科学计数法来表示。当变量实际值只有9位而设置精度输出10位时,输出结果并不会补0。可以使用fixed来实现指定输出几位小数。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double x = 123.456789;
cout << setprecision(0)<< x << endl; // 输出结果为1e+02
cout << setprecision(1)<< x << endl; // 输出结果为1e+02
cout << setprecision(2)<< x << endl; // 输出结果为1.2e+02
cout << setprecision(3)<< x << endl; //输出结果为123
cout << setprecision(4)<< x << endl; //输出结果为123.5
cout << setprecision(10)<< x << endl; //输出结果为123.456789不会补零
cout << fixed << setprecision(0)<< x << endl; //输出结果为123
cout << fixed << setprecision(2)<< x << endl; //输出结果为123.46
cout << fixed << setprecision(10)<< x << endl; //输出结果为123.4567890000会补零
return 0;
}
- printf和scanf
cin和cout时间效率慢,忽略一切回车和空格符号。scanf和printf时间效率快,输入char类型的变量时不忽略空格和回车。以下是scanf和printf输入输出不同类型变量的示例。
#include<bits/stdc++.h>
int main()
{
//输入输出float
//输入float,int或double类型时,scanf会自动忽略空格和回车
float a,b;
scanf("%f%f",&a,&b);//默认空格和回车都是分隔符
printf("%.1f\n%.2f",a+b,a*b);
//输入输出char
//输入char时,scanf不会自动忽略空格和回车,会吸收上面遗留的空格或回车
//上面输入b后,再从键盘输入的任何字符都会被被后面的scanf吸收
//例如输入:1.1 空格 1.2 空格 回车,那么1.2之后的空格会赋值给c,回车会赋值给d
getchar();//吸收掉上一行的回车
char c,d;
scanf("%c%c",&c,&d);
printf("%c\n%c",c,d);
//输入输出double
getchar();
double e,f;
scanf("%lf%lf",&e,&f);
printf("%.1lf\n%.2lf",e+f,e*f);
//输入输出long long
getchar();
long long g,h;
scanf("%lld%lld",&g,&h);
printf("%lld\n%lld",g+h,g*h);
//输入输出char[]
getchar();
char str1[100],str2[100];
scanf("%s%s",str1,str2);
printf("%s\n%s",str1,str2);
}
scanf和printf一堆占位符十分复杂,但是对于超大数据集使用cin和cout又容易超时。为了使用cin和cout进行输入输出又希望其效率提高,可以加上以下三条语句:
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
使其效率与scanf和printf相差无几。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
- 变量类型
关键字 | 值 | 空间(1byte=8bit) |
bool | true或false | 1byte |
char | 1byte | |
int | -231~231-1 | 4byte |
float | 6位有效数字 | 4byte |
double | 15位有效数字 | 8byte |
long long | -263~263-1 | 8byte |
long double | 18位有效数字 | 12byte |
-
- bool类型
bool类型的变量,以非零数(包括负数、小数)赋值结果都会变为true(1),以0赋值结果会为false(0)
#include<iostream>
using namespace std;
int main()
{
bool flag1=-3.1; // 非0是true
bool flag2=0.0; // 0.0是false
bool flag3=0; // 0是false
bool flag4=true; // true是true
cout<<flag1<<endl; // 输出1
cout<<flag2<<endl; // 输出0
cout<<flag3<<endl; // 输出0
cout<<flag4<<endl; // 输出1
}
输出结果为:1,0,0,1
-
- float和double
float类型能存6位有效数字即只有前6位是准确的,第6位以后的数字是不准确的。double类型能存15位有效数字即只有前15位是有效的。
#include<iostream>
#include <iomanip>
using namespace std;
int main()
{
float x=123.456789;
double y=123.456789;
cout<<setprecision(10)<<x<<endl;
// 输出结果为123.4567871,这是因为x是float型,只有前6位是有效的,后面的数字都是不准确的
cout<<setprecision(10)<<y<<endl;
// 输出结果为123.456789,这是因为y是double型,有前15位有效数字,所以输出结果是准确的
不同类型的变量可以直接进行比较运算,因为它们在进行比较时会先隐式转换成相同类型。但当比较浮点数和整数时,应谨慎处理,尤其是当涉及较大的数或精度较高时,由于浮点数的精度限制,可能会出现意料之外的结果。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a=1;
double b=1.0;
float c=1.0;
double d=1.0000001;
if(a==b) cout<<"a==b"<<endl;
else cout<<"a!=b"<<endl; // 输出a==b
if(a==c) cout<<"a==c"<<endl;
else cout<<"a!=c"<<endl; // 输出a==c
if(b==c) cout<<"b==c"<<endl;
else cout<<"b!=c"<<endl; // 输出b==c
if(a>d) cout<<"a>d"<<endl;
else cout<<"a<d"<<endl; // 输出a<d
}
- 数组
同普通变量一样,当数组在函数外部定义为全局变量时,如果不赋初值,则其所有元素初值被自动初始化成0。当在内部定义时,如果不赋初值,则元素值随机。如果定义时只赋值了几项,那么后面其余项都会被自动设置成0。
#include<bits/stdc++.h>
using namespace std;
int a[100];// 全局变量,a数组内所有元素自动初始化为0
int main()
{
int b[100];// 局部变量,定义时没有赋初值,元素值随机
int c[100]={1};// 局部变量,定义时第一个元素被赋初值为1,其余元素自动初始化为0
cout<<a[0]<<endl;// 输出0
cout<<b[0]<<endl;// 输出随机值
cout<<c[0]<<endl;// 输出1
cout<<c[1]<<endl;// 输出0
}
- 数组的memset与memcopy操作
如果要将数组内的每个元素都赋值为一个值,可以直接使用for循环将每个变量进行赋值操作。使用<cstring>文件中函数memset函数会更简便。该函数接受3个参数分别是数组名,按字节将元素每个byte设置的目标值,数组的总长度。由于memset函数是按字节初始化,一般只利用此函数将数组内所有元素初始化位0或者-1。
#include<bits/stdc++.h>
using namespace std;
int a[100];// 全局变量,a数组内所有元素自动初始化为0
int main()
{
memset(a,0,sizeof a);
//m第二个参数是指按字节初始化
//a数组元素是int类型,一个int类型占4byte即32bit
//按字节初始化即每位byte都初始化为0,
//其二进制表示位00000000 00000000 00000000 00000000
//所以memset操作后a数组内所有元素都变成了0
memset(a,1,sizeof a);
//按字节初始化即每位byte都初始化为1,
//其二进制表示位00000001 00000001 00000001 00000001
//所以memset操作后a数组内所有元素都变成了16843009
memset(a,-1,sizeof a);
//由于-1在计算机内的特殊表示,即所有位都为1
//memset操作后a数组内所有元素都变成了-1
}
使用<cstring>文件中的memcopy函数可以简便的实现数组的复制。该函数接受3个参数分别是需要改变的元素值的数组名,需要作为被复制对象的数组名,数组总长度。
#include<bits/stdc++.h>
using namespace std;
int a[100];
int b[100];
int main()
{
memset(a,-1,sizeof a);//memset操作后a数组内所有元素都变成了-1
memcopy(b,a,sizeof a);//memcopy操作后b数组内所有元素都变成了-1
}
- string类型
- string和char[]的
string作用和char[]一样用于字符串类型。string类型的字符串变量可以直接进行比较,直接相互赋值,加法拼接,但不能使用减法。
string a="abce";
string b="abcdef";
string c=a+b;
if(a<b) cout<<"a<b"<<endl;
else cout<<"a>b"<<endl;// 输出a>b
cout<<c<<endl;// 输出abceabcdef
如果要从字符串第2个字母开始输入或者输出,对于char[]字符串,无论是cin、cout还是scanf、printf,都可以直接在输入或者输出的变量名后加1,从而使字符串下标从1开始。而string类型的字符串不支持这类操作。
char a[100];
char b[100];
cin>>a+1;// 下标从1开始,输入abcdef
scanf("%s",b+2);// 下标从2开始,输入123456789
cout<<a+1<<endl;// 输出abcdef
cout<<a+2<<endl;// 输出bcdef
printf("%s\n",b+2);// 输出123456789
string类型的变量只能使用cin和cout来输入输出,不能使用scanf或者printf来输入输出。输入char[]或string类型符串时,无论是scanf还是cin都是以空格或者回车作为分隔符号,即忽略任何回车和空格符号,无法读入一整行带有空格的字符串。
char c[100];
cin>>c;//输入hello world
cout<<c<<endl;//输出hello
如果要读入一整行的字符串,可以使用getline(cin,s)函数,参数是cin和字符串名,但此函数只能用于string类型的字符串。
string c;
getline(cin,c);//输入hello world
cout<<c<<endl;//输出hello world
如果实在想用getline输入char[],可以通过cin.getline(s,100),参数是数组名和数组长度。输入char[]也可以通过fgets(s,100,stdio)函数来读入一整行带空格字符串,此函数只适用char[]字符串。虽然getlin和fgets都会以回车作为分隔符,但fgets会把行末的回车键读入并保存于字符串中,而getlin会把行末的回车符读入并丢弃。
char c[100];
char s[100];
fgets(c,100,stdin);//输入hello world并按下回车
cin.getline(s,100);//输入123 456并按下回车
//c=“hello world\n” 而 s=“123 456”
cout<<c<<endl;//输出hello world和一个换行
printf("%s",s);//输出123 456
-
- char[]的处理函数
<cstring>中包括3个常用的处理char[]的函数,分别是strlen,strcmp和strcpy
Strlen(a)用于返回字符串a的长度,并且此长度不包括字符串结束符/0
char c[100]="abc";
int len=strlen(c);
cout<<c<<endl;//输出abc
cout<<len<<endl;//输出3
strcmp(a,b)用于比较两个字符串a和b,返回-1或0或1。
char a[100]="abc";
char b[100]="def";
int res=strcmp(a,b);
cout<<res<<endl;//输出-1
strcpy(a,b)用于把后者字符串b赋值给前者字符串a。
char a[100]="abc";
char b[100]="def";
strcpy(a,b);
cout<<a<<endl;//输出def
cout<<b<<endl;//输出def
-
- string的处理函数
substr(i,len)函数用于返回一个子串,该子串是原串下标从i开始截取长度为len的子串,如果不给出第二个参数len或者len太大超过原串长度,则字串截取到原串末尾。
string a="abcdefgh";
string b=a.substr(2,3);//从下标为2的位置开始,截取3个字符
cout<<b<<endl;//输出cde
string c=a.substr(1);//从下标为1的位置开始,截取到最后
cout<<c<<endl;//输出bcdefgh
c_str()可以返回字符串首地址,这样就可以通过printf或者puts来输出string类型的字符串。
string a="abc";
string b="def";
printf("%s\n",a.c_str());//输出abc
puts(b.c_str());//输出def
size()和length()都可以以O(1)的复杂度返回字符串的长度。
string a="abcdef";
string b;
cout<<a.length()<<endl;//输出6
cout<<b.size()<<endl;//输出0
pop_back()可删除字符串末尾字符,用于去掉末尾多余空格或其他字符。
string a="abc ";//末尾有空格
a.pop_back();//删除末尾的空格
cout<<a<<endl;//输出abc
erase(i,n)可以删除字符串从下标i开始的n个字符。
string s="123456789";
s.erase(3,2);//删除45
cout<<s<<endl;//输出1236789
- vector
size()函数返回容器大小,这个函数适用于所有容器。vector如同数组一样可以通过[]随机存取,也可以直接相互赋值,可以进行比较运算。vector可以在定义时在变量名后加上圆括号指定初始大小和初始值。即使是在函数内部定义,如果不指定初值依然自动初始化为0。也可以使用resize()函数指定大小。
vector<int>a,b(8,2),c(10);
cout<<a.size()<<endl;//输出0
cout<<b.size()<<endl;//输出8
cout<<c.size()<<endl;//输出10
b.resize(5);//重新设置b的大小为5
cout<<b.size()<<endl;//输出5
cout<<b[0]<<endl;//输出2
cout<<c[0]<<endl;//输出0
push_back(x)和pop_back()用于在vector末尾加上元素x或者删除末尾元素。
vector<int> a;
a.push_back(1);
a.push_back(2);
cout<<a.size()<<endl;//输出2
cout<<a[0]<<endl;//输出1
cout<<a[1]<<endl;//输出2
a.pop_back();
cout<<a.size()<<endl;//输出1
cout<<a[0]<<endl;//输出1
front()和back()用于取出vector首元素和末尾元素
vector<int> a={1,2,3};
cout<<a.front()<<endl;//输出1
cout<<a.back()<<endl;//输出3
vector的遍历可以直接像普通数组那样用一个变量i作为下标直接访问,但是对于set和map无法通过下标直接存取,遍历它们则需要使用迭代器来遍历。迭代器的定义如下,由于比较长也可以直接使用关键字auto代替。
vector<int> a={1,2,3};
//定义一个vector容器的迭代器it 遍历容器a
for(vector<int>::iterator it=a.begin();it!=a.end();it++)
{
cout<<*it<<endl;//输出了1,2,3
}
//利用auto自动识别类型定义一个vector容器的迭代器x 遍历容器a
for(auto x=a.begin();x!=a.end();x++)
{
cout<<*x<<endl;//输出了1,2,3
}
begin()和end()可以返回容器的首尾迭代器,begin()指向容器的第一个元素,end()指向容器最后一个元素的下一个位置。迭代器如同指针一样可以与整数加减,使得迭代器移动,也可以进行++操作--操作。如果两个迭代器相减会得到两个迭代器对应下标之间的距离。
vector<int> a={1,2,3,4,5,6};
vector<int>::iterator x1=a.begin();//x1指向a的第一个元素
vector<int>::iterator x2=a.end();//x2指向a的最后一个元素的下一个位置
cout<<*x1<<endl;//输出第一个元素1
x1=x1+2;//指向第三个元素
cout<<*x1<<endl;//输出第三个元素3
cout<<x2-x1<<endl;//x2与x1之间的距离为4,输出4
- queue
front()和back()用于取出队首和队尾元素,push(x)和pop()用于从队尾插入x和从队头弹出队头元素。
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout<<q.front()<<endl;//输出队首元素1
cout<<q.back()<<endl;//输出队尾元素3
q.pop();//弹出队首元素1,队首元素变成了2
cout<<q.front()<<endl;//输出队首元素2
cout<<q.back()<<endl;//输出队尾元素3
- priority_queue
定义大小根堆的方法如下,第一个参数表示堆中元素类型,其他参数固定
priority_queue<int> q1;//默认是大根堆
priority_queue<int,vector<int>,greater<int> > q2;//小根堆
push()和pop()实现插入元素和删除元素,top()用于取堆顶元素。
priority_queue<int> q1;//默认是大根堆
priority_queue<int,vector<int>,greater<int> > q2;//小根堆
q1.push(1);
q1.push(2);
q1.push(3);
q2.push(1);
q2.push(2);
q2.push(3);
cout<<q1.top()<<endl;//大根堆输出3
cout<<q2.top()<<endl;//小根堆输出1
定义大小根堆时,堆内元素必须可以比较大小,如果无法比较大小例如是自己定义的结构体类型,则需要重载符号,大根堆需要重载小于号,小根堆需要重载大于号。
struct stu
{
int a,b;
//大根堆重载小于号
bool operator < (const stu &s) const
{
return a<s.a;
}
};
priority_queue<stu> q;//大根堆内元素是stu类型
q.push({1,5});
q.push({3,3});
auto t=q.top();//堆顶元素是{3,3}
cout<<t.a<<endl;//输出3
cout<<t.b<<endl//输出3
- stack
top()用于取出栈顶元素,push(x)和pop()用于从栈顶插入x和弹出栈顶元素。
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
cout<<s.top()<<endl;//输出3
s.pop();
cout<<s.top()<<endl;//输出2
- deque
支持随机存取[]操作,相当于可以在容器首部加入元素与删除元素的vector plus版,支持vector的所有操作,并且还提供两个vector不支持的操作,在首部插入元素push_front()和在首部删除元素pop_front()
deque<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_front(4);
//此时a内元素为4123
cout<<a.front()<<endl;//输出4
cout<<a.back()<<endl;//输出3
a.pop_back();
a.pop_front();
//此时a内元素为12
cout<<a.front()<<endl;//输出1
cout<<a.back()<<endl;//输出2
- set
set用于动态维护有序集合,所以set内的元素必须可以进行大小比较,否则需要自定义重载小于号。set内不含有重新元素,如果插入一个已存在的元素则会忽略这个操作。multiset是元素可以重复set。set也支持迭代器的操作,但是迭代器的++或--操作的时间复杂度为O(log2n)
insert(x)用于插入入元素x,时间复杂度为O(log2n)。find(x)会返回值为x的迭代器,时间复杂度为O(log2n),若不存在值为x的元素,则返回末元素的下一个位置的迭代器,即end()迭代器,可以利用find(x)==s.end()来判断是否存在值为x的元素。
set<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(1);//插入重复的元素会忽略此操作
auto it=s.find(2);//查找元素2
cout<<*it<<endl;//输出2
it=s.find(233);//查找元素233
if(it==s.end()) cout<<"not found"<<endl;
else cout<<"found"<<endl;//输出not found
erase()用于删除元素,如果提供的参数是元素值x,则会删除集合内所有值为x的元素,时间复杂度为O(log2n+k)。如果提供的参数是迭代器,则只会删除迭代器所指向的元素,时间复杂度为O(log2n)。lower_bound(x)返回第一个大于等于x的元素的迭代器。upper_bound(x)返回第一个大于x的元素的迭代器,时间复杂度为O(log2n)
multiset<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(1);
s.insert(1);
//此时s中的元素为1 1 1 2 3
auto it1=s.lower_bound(2);//返回第一个大于等于2的元素的迭代器
cout<<*it1<<endl;//输出2
auto it2=s.upper_bound(2);//返回第一个大于2的元素的迭代器
cout<<*it2<<endl;//输出3
s.erase(s.begin());//删除第一个元素
//此时s中的元素为1 1 2 3
cout<<*s.begin()<<endl;//输出1
s.erase(1);//删除所有值为1的元素
//此时s中的元素为2 3
cout<<*s.begin()<<endl;//输出2
count(x)用于返回值为x的元素的个数,时间复杂度为O(k+log2n)
multiset<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(1);
s.insert(1);
//此时s中的元素为1 1 1 2 3
int x=s.count(1);//返回1的个数
cout<<x<<endl;//输出3
unordered_set和underored_multiset是无序的set和multiset,不支持lower_bound()和upper_bound()函数。
- map
map中的key必须定义小于运算符可进行比较。map支持[]随机存取,时间复杂度为O(log2n)。可以之间通过类似数组的方式mp[2]=3直接赋值添加元素。find(key)可以查找键为key的元素,返回该元素的迭代器,但是无法直接通过*it输出,因为迭代器指向的是一个二元组,可以通过it->first和it->second来输出。count(key)可以返回键值为key的元素个数。
map<int,int> a;
a[1]=9;
a[2]=8;
a[3]=7;
a[1]=1;//会覆盖之前的值
a.insert({4,6});
auto it=a.find(1);
cout<<it->first<<" "<<it->second<<endl;//输出1 1
与set类似,erase()函数可以接受迭代器作为参数或者元素的key作为参数,都能达到删除相应元素的效果。
map<int,int> a;
a[1]=9;
a[2]=8;
a[3]=7;
a.insert({4,6});
auto it=a.find(1);
a.erase(2);
a.erase(it);
if(a.find(1)==a.end()||a.find(2)==a.end())//1和2都查找不到
cout<<"not found 1 and 2"<<endl;
else
cout<<"find 1 or 2"<<endl;//输出not found 1 and 2
lower_bound(key)和upper_bound(key)的用法也和set类似。
map<int,int> a;
a[1]=9;
a[2]=8;
a[3]=7;
a.insert({4,6});
auto it1=a.lower_bound(2);//返回第一个大于等于2的迭代器
auto it2=a.upper_bound(2);//返回第一个大于2的迭代器
cout<<it1->first<<" "<<it1->second<<endl;//输出2 8
cout<<it2->first<<" "<<it2->second<<endl;//输出3 7
unordered_map和underored_multimap是无序的map和multimap,不支持lower_bound()和upper_bound()函数。
- bitset
bitset是一串由0和1组成的二进制串,可以看作是元素值只能为0或者1的整型数组。是一种节省空间的存储二进制数的容器。当没有赋值时其内元素自动设置为0。与其他容器的定义不同,中间是容器长度而不是元素类型。
bitset<10000> a;
a[0]=1;
cout<<a[0]<<endl;//输出1
cout<<a[1]<<endl;//输出0
count()返回bitset串中的1的个数,bitset串可以直接进行位运算。
bitset<10000> a,b;
a[0]=1;
a[1]=1;
b[0]=1;
b&=a;//可以进行位运算
b|=a;
cout<<a.count()<<endl;//输出2
cout<<b.count()<<endl;//输出2
- pair
pair是一个二元组,可以通过first和second取出二元组的第一个元素和第二个元素。支持比较运算,先比较第一个关键子再比较第二个关键字。
pair<int,string> a,b;
a={1,"def"};
b={2,"abc"};
cout<<a.first<<' '<<a.second<<endl;//输出1 def
if(a>b) cout<<"a>b"<<endl;
else cout<<"a<b"<<endl;//输出a<b
- 位运算
由于优先级比较复杂,对两个数位运算时带上括号。
与运算&:两位同时为1则结果为1,否则结果为0
或运算|:两位同时为0则结果为0,否则结果为1
取反运算~:0则变为1,1则变为0
异或运算^:两位相同则结果为0,两位不同则结果为1
右移>>:右移k位等价于除以2k
左移<<:左移k位等价于乘以2k
通过>>和&操作,可以查看一个数第k位(从右往左数)是0还是1。
int a=97;
cout<<(a>>2&1)<<endl;//97的二进制为1100001,第2位为0,所以输出0
通过&和~操作,可以实现lowbit()运算,返回一个数的最后一位1。
int x=92;//92的二进制表示是1011100
cout<<(x&-x)<<endl;//输出100即4
//-x和~x+1是相等的
cout<<(x&(~x+1))<<endl;//输出100即4
- 范围for循环和引用
引用可以看作某个变量的别名,通过修改这个引用可以修改原始变量,以下以string和vector为例展示范围for循环,其余容器类似。
//string的范围for循环
string a="abcdef";
for(char x:a)//x是a中的每一个元素,是char类型,char可以换成auto
{
cout<<x<<endl;//输出abcdef
}
//等价于下面的循环
for(int i=0;i<a.size();i++)
{
int x=a[i];
cout<<x<<endl;//输出abcdef
}
//通过引用来修改字符串a的值
for(auto &i:a)
{
i='x';
}
cout<<a<<endl;//输出xxxxxx
//vector的范围for循环
vector<int> b={1,2,3,4,5};
for(int x:b)//x是b中的每一个元素,是int类型,int可以换成auto
{
cout<<x<<endl;//输出12345
}
- 常用库函数
- to_string()
to_string()的头文件是<string>,可以将int,float和double等其他类型的常量或者变量转化为string类型的变量。但对与float和double转化为string后会出现一些零填充的情况,需要手动去除末尾填充的0。
string s1,s2;
int a=123;
s1=to_string(a);
s2=to_string(123.456);
cout<<s1<<endl;//输出123
cout<<s2<<endl;//输出123.456000
for(int i=s2.size()-1;i>=0;i--)
{
if(s2[i]=='0')//删除末尾的‘0’
s2.erase(i,1);
else if(s2[i]=='.')//删除末尾的‘.’
s2.erase(i,1);
else
break;
}
cout<<s2<<endl;//输出123.456
与to_string()类似,stoi(),stod(),stof()可以将string类型字符串转化为int,double或者float。
string s1="456.789";
string s2=".456";
string s3="45a6.123";
int a=stoi(s1);
int b=stoi(s3);
double c=stod(s1);
double d=stod(s2);
double e=stod(s3);
cout<<a<<endl;//输出456
cout<<b<<endl;//输出45
cout<<c<<endl;//输出456.789
cout<<d<<endl;//输出0.456
cout<<e<<endl;//输出45
-
- cctype中的函数
使用头文件<cctype>中一些简单的函数可以简化代码。
函数名 | 功能 |
isalpha | 判断是否是字母 |
islower | 判断是否是小写字母 |
isupper | 判断是否是大写字母 |
isalnum | 判断是否是字母或数字 |
toupper | 将字母大写 |
tolower | 将字母小写 |
isdigit | 判断是否是数字 |
char c1='A';
char c2='b';
char c3='#';
if(isupper(c1)) cout<<"c1是大写字母"<<endl;
else cout<<"c1不是大写字母"<<endl;//输出c1是大写字母
if(isalpha(c2)) cout<<"c2是字母"<<endl;
else cout<<"c2不是字母"<<endl;//输出c2是字母
if(isdigit(c3)) cout<<"c3是数字"<<endl;
else cout<<"c3不是数字"<<endl;//输出c3不是数字
char c4=tolower(c1);//转化为小写字母
char c5=toupper(c3);//不合法则返回原字符
cout<<c4<<endl;//输出a
cout<<c5<<endl;//输出#
-
- reverse()
reverse()的头文件是<algorithm>,用于翻转容器内的元素。
//vector的翻转
vector<int> a={1,2,3,4,5,6,7,8,9,10};
reverse(a.begin(),a.end());
for(auto i:a)
cout<<i<<" ";//输出10 9 8 7 6 5 4 3 2 1
//数组的翻转
int b[10]={1,2,3,4,5,6,7,8,9,10};
reverse(b,b+10);
for(auto i:b)
cout<<i<<" ";//输出10 9 8 7 6 5 4 3 2 1
-
- unique()
unique()的头文件是<algorithm>,用于对有序容器去重,并返回去重后新容器的尾迭代器。unique()去重后返回的迭代器减去原容器尾迭代器的差值即为重复元素个数。
int a[7]={1,1,2,2,3,3,4};
int cnt=unique(a,a+7)-a;
cout<<cnt<<endl;//输出4
for(int i=0;i<cnt;i++)
cout<<a[i]<<" ";//输出1 2 3 4
对于vector,可以调用erase()函数实现去重同时删除重复元素。
vector<int> v={1,1,2,2,3,3,4};
v.erase(unique(v.begin(),v.end()),v.end());//去重并删除多余元素
for(auto i:v)
cout<<i<<" ";//输出1 2 3 4
-
- sort()
sort()的头文件是<algorithm>,实现容器内元素从小到大排序。如果要从大到小排序需要提供第三个参数greater<int>()。
int a[5]={1,1,5,4,3};
sort(a,a+5,greater<int>());
for(int i=0;i<5;i++)
cout<<a[i]<<endl;//输出结果为5 4 3 1 1
如果需要自定义大小排序,则可以自定义一个bool类型的比较函数,返回结果是a是否应该排在b前面,并以函数名作为sort()的第三个参数。
#include<bits/stdc++.h>
using namespace std;
struct Stu
{
int x,y;
}a[5];
bool cmp(Stu a,Stu b)//从大到小排序
{
return a.x<b.x;
}
int main()
{
for(int i=0;i<5;i++)
{
a[i].x=-i;
a[i].y=i;
}
sort(a,a+5,cmp);
for(int i=0;i<5;i++)
cout<<"{"<<a[i].x<<" "<<a[i].y<<"}"<<endl;//输出{-4 4}{-3 3}{-2 2}{-1 1}{0 0}
}
也可以在结构体内重载小于号而不必自定义比较函数。
-
- lower_bound()和upper_bound()
它们的头文件是<algorithm>,这两个函数除了作为set和map的内部函数,还可以用于二分查找其他容器。
int a[5]={1,2,4,5,6};
int *p=lower_bound(a,a+5,3);//获取第一个大于等于3的元素地址
int t=upper_bound(a,a+5,3)-a;//获取第一个大于3的元素下标
cout<<*p<<endl;//输出4
cout<<t<<endl;//输出2
vector<int> v={1,2,4,5,6};
auto p1=lower_bound(v.begin(),v.end(),3);//获取第一个大于等于3的元素地址
auto t1=upper_bound(v.begin(),v.end(),3)-v.begin();//获取第一个大于3的元素下标
cout<<*p1<<endl;//输出4
cout<<t1<<endl;//输出2