10分钟重温c++语法

c++语法基础

 #include <iostream>
 ​
 using namespace std;
 ​
 int main(){
     cout << "Hello World" << endl;
     
     return 0;
 }

入门及简单的顺序结构

1、变量的定义

变量类型:

类型关键字大小
布尔型bool1btye
字符型char1btye
整型int4btye
浮点型(单精度)float4btye
双浮点型(双精度)double8btye

变量必须先定义,才可以使用。不能重名。

 int main() {
     int a, b = 2, c = b;
     float d = 1.5, f = 1.23e2;
     bool g = true;
     char j = 'a';
 ​
     return 0;
 }
2、输入输出
 int main() {
     int a, b;
     cin >> a >> b;//输入
     cout << a + b <<' '<< a * b << endl;//输出,endl是回车
     //先输出a+b,再输出空格,再输出a*b,最后回车
 ​
     return 0;
 }
 int main() {
     int a, b;
     scanf("%d%d", &a, &b);
     printf("%d %d", a + b, a * b);
     printf("a+b=%d\na*b=%d", a + b, a * b);
     float c;
     scanf("%f",&c);
     printf("a+c=%.2f",a+c);
 ​
     return 0;
 }
 /*
  * int:%d
  * float:%f
  * double:%lf
  * char:%c
  * long long:%lld
  * bool:用0和1表示
  */

scanf会把空格读入,cin不会读入空格。

printf拓展:

  • Float, double 等输出保留若干位小数时用:%.4f, %3lf

  • %8.3f, 表示这个浮点数的最小宽度为 8,保留 3 位小数,当宽度不足时 在前面补空格。

  • %-8.3f,表示最小宽度为 8,保留 3 位小数,当宽度不足时在后面补上空 格

  • %08.3f, 表示最小宽度为 8,保留 3 位小数,当宽度不足时在前面补上 0

3、表达式
 int main() {
     int a = 6 + 3 * 4 / 2 - 2;
     cout << a << endl;
     int b = a * 10 + 5 / 2;
     cout << b << endl;
     cout << 23 * 56 - 78 / 3 << endl;
     
     cout << 5 % 2 << endl;//1   正数取模为正
     cout << -5 % 2 << endl;//-1   负数取模为负
 ​
     return 0;
 }
4、变量类型转化
 int main() {
     float x = 123.12;
     int y = (int) x;
     cout << x << ' ' << y << endl;//向下取整
 ​
     return 0;
 }

隐式转换:低精度转为高精度

5、常量的定义
 #include <iostream>
 #include <cstdio>
 ​
 using namespace std;
 ​
 const double eps=1e-6;   //定义常量
 ​
 int main() {
     ……
     return 0;
 } 
浮点数的比较
 int main() {
     if (sqrt(3) * sqrt(3) - 3 <= eps) {
         printf("equal");
     } else {
         printf("no");
     }
 ​
     return 0;
 }

判断结构

1.基本 if-else 语句:

当条件成立时,执行某些语句;否则执行另一些语句。else语句可以省略

 int main() {
     int score;
     cin >> score;
     if (score >= 60) {
         cout << "及格" << endl;
     } else {
         cout << "不及格" << endl;
     }
 ​
     return 0;
 }
2.if-else if-else语句:
 int main() {
     int score;
     cin >> score;
     if (score >= 85) cout << 'A' << endl;
     else if (score>=60) cout << 'B' << endl;
     else cout<<'C'<<endl;
     
     return 0;
 }
3.条件表达式

与 && 或 || 非 !

循环

1.while循环
 int main() {
     int i=1,sum=0;
     while(i<=100){
         sum+=i;
         i++;
     }
     printf("%d",sum);
     return 0;
 }

求斐波那契数列的第 n 项。f(1)=1, f(2)=1, f(3)=2, f(n)=f(n-1) + f(n-2)。

 int main() {
     int n;
     cin>>n;
     int a=1,b=1,i=1;
 ​
     while(i<n){
         int c=a+b;
         a=b;
         b=c;
         i++;
     }
     cout<<a<<endl;
 ​
     return 0;
 }
2.do while
 int main() {
     int n=10,i=1,sum=0;
     do{
         sum+=i;
         i++;
     }while(i<=10);
 ​
     printf("%d",sum);
     return 0;
 }
3.for循环

for(初始化语句;条件语句;表达式)语句;

 int main() {
     int i,sum;
     for(i=1,sum=0;i<=100;i++){
         sum+=i;
     }
     printf("%d",sum);
     return 0;
 }
4.continue语句
 int main() {
     int i,sum;
     for(i=1,sum=0;i<=100;i++){
         if(i%2==0){
             continue;
         }
         sum+=i;
     }
     printf("%d",sum);
     return 0;
 }
5.break语句
 int main() {
     int i,sum;
     for(i=1,sum=0;i<=100;i++){
         if(sum>2000){
             break;
         }
         sum+=i;
     }
     printf("%d",sum);
     return 0;
 }

数组

1.一维数组
1.1数组初始化
 int main() {
     int a[10],b[20];
     float f[33];
     double d[123];
     char c[21];
 ​
     return 0;
 }
1.2数组初始化
 int main() {
     int a[3]={0,1,2};
     int b[]={0,1,2};
     int c[5]={0,1,2};           //等价于c[]={0,1,2,0,0},默认值为0
     char d[3]={'a','b','c'};    //字符数组初始化
 ​
     int f[10]={0};              //将数组全部初始化为0的写法
     return 0;
 }

函数内部定义数组有长度限制,如果数组太长运行报错(超出栈长),可以将数组定义在函数外(堆)。

函数内部定义的变量(局部变量)为随机值,函数外面定义的变量(全局变量)都是0。

1.3数组下标引用
 int main() {
     int a[3] = {0, 1, 2};
     cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl;
     a[0] = 5;
     cout << a[0] << endl;
 ​
     return 0;
 }
1.4翻转数组
 //头文件
 #include <algorithm>
 //使用方法
 reverse(a, a+n);//n为数组中的元素个数
 ​
 reverse(a,a+n);//翻转整个数组
 reverse(a,a+k);//翻转前k个元素
 reverse(a+k,a+n);//反转后n-k个元素

1.5高精度计算
 #include <iostream>
 #include <cstdio>
 ​
 using namespace std;
 ​
 int main() {
     int a[10000] = {1};
     int n, size = 1;
     cin >> n;
     while (n--) {
         int t =0;
         for (int i = 0; i < size; i++) {
             t+=a[i]*2;
             a[i]=t%10;
             t/=10;
         }
         if(t) a[size++]=1;
     }
     for (int i = size - 1; i >= 0; i--) {
         cout << a[i];
     }
 ​
     return 0;
 }

2.多维数组
1.1数组定义
 int main() {
     int a[3][4];
     int arr[10][20][30]={0};
     int b[3][4]={
             {0,1,2,3},
             {4,5,6,7},
             {8,9,10,11}
     };
     for(int i=0;i<3;i++){
         for(int j=0;j<4;j++){
             cout<<b[i][j]<<' ';
         }
     }
 ​
     return 0;
 }
1.2初始化数组
 #include <iostream>
 #include <cstring>//头文件
 ​
 using namespace std;
 ​
 int main() {
     int a[10];
     memset(a,0,40);//数组的名字,初始化内容(把每一个字节赋值为0或其他),初始化的长度(以字节为单                      位,一个int占4个byte)
     memset(a,-1,sizeof a);
     
     for (int i = 0;i<10;i++){
         cout<<a[i]<<' ';
     }
 ​
     return 0;
 }
1.3复制数组
 #include <iostream>
 #include <cstring>
 ​
 using namespace std;
 ​
 int main() {
     int a[10],b[10];
 ​
     for (int i=0;i<10;i++){
         a[i]=i;
     }
     
     memcpy(b,a,sizeof a);//目标数组,原数组,长度
     
     return 0;
 }

字符串

1.字符与整数的联系——ASCII 码

每个常用字符都对应一个-128~127 的数字,二者之间可以相互转化:

 int main() {
     char c = 'a';
     cout << (int) c << endl;
 ​
     cout << (char) 97 << endl;
 ​
     return 0;
 }

常用 ASCII 值:’A’-‘Z’ 是 65~90,’a’-‘z’是 97-122,’0’-‘9’是 48-57。 字符可以参与运算,运算时会将其当做整数:

 int main() {
     int a='B'-'A';
     int b='A'*'B';
     char c='A'-2;
 ​
     cout<<a<<endl;
     cout<<b<<endl;
     cout<<c<<endl;
 ​
     return 0;
 }

2.字符数组

字符串就是字符数组加上结束符’\0’。 可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个’\0’字符,因此字符数组的长度至少要比字符串的长度多 1!

 int main() {
     char a1[]={'C','+','+'};
     char a2[]={'C','+','+','\0'};
     char a3[]="C++";       //a[2],a[3]等价
     char a4[6]="abcdef";   //错误:没有空间存放空字符
     
     return 0;
 }
2.1字符数组输入输出
 int main() {
     char str[100];
     cin>>str;                   //输入时,遇到空格或回车就会停止
     char str[100];
     scanf("%s",str);            //注:不加&
     
     cout<<str<<endl;           
     printf("%s\n",str);         //输出时,遇到空格或回车不会停止
     puts(str);
 ​
     return 0;
 }

读入一行字符串,包括空格:

 int main() {
     char str[100];
     fgets(str,100,stdin);   //最大长度为100   //会读入回车(不建议使用)
     cin.getline(str,100);
     
     cout << str << endl;
     
     string s;
     getline(cin,s);         //必须是string类型
     cout<<s<<endl;
 ​
     return 0;
 }

从某个位置开始输出:

 int main() {
     char str[] = "abcdefgh";
 ​
     cout << str << endl;        //从a开始输出
     cout << str + 1 << endl;    //从b开始输出(从下标1开始输出)
 ​
     return 0;
 }

数组从下标[1]开始:

 int main() {
     char str[100];
     scanf("%s",str+1);
     cin>>str+1;
 ​
     cout<<str<<endl;
     return 0;
 }
2.2 字符数组的常用操作

下面几个函数需要引入头文件:

#include <string.h><cstring> (1) strlen(str),求字符串的长度

(2) strcmp(a, b),比较两个字符串的大小,a < b 返回-1,a == b 返回 0,a > b 返回 1。这里的比较方式是字典序!

(3) strcpy(a, b),将字符串 b 复制给从 a 开始的字符数组。

3.3遍历字符数组中的字符:
 int main() {
     char a[100]="hello world";
 ​
     for(int i=0;i<strlen(a);i++){       //双层循环,每次strlen都要遍历一次
         cout<<a[i]<<endl;
     }
 ​
     for(int i=0,len=strlen(a);i<len;i++){     //优化
         cout<<a[i]<<endl;
     }
     
     return 0;
 }

3.标准库类型string

可变长的字符序列,比字符数组更加好用。需要引入头文件: #include <string>

3.1 定义和初始化
 int main() {
     string s1;         //默认初始化,s1是一个空字符串
     string s2=s1;      //s2是s1的副本
     string s3="hello"; //s3是该字符串字面值的副本
     string s4(10,'c'); //s4的内容是cccccccccc
 ​
     return 0;
 }
3.2 string 上的操作

(1)输入输出操作

 int main() {
     string s1, s2;
     cin >> s1 >> s2;
     cout << s1 << ' ' << s2 << endl;
     printf("%s\n",s1.c_str());
 ​
     return 0;
 }

注意:不能用scanf输入;不能用 printf 直接输出 string,需要写成:printf(“%s”, s.c_str())

(2)使用 getline 读取一整行:

 int main() {
     string s1, s2;
     getline(cin, s1);
     cout << s1 << endl;
 ​
     return 0;
 }

(3)string 的 empty 和 size 操作(注意 size 是无符号整数,因此 s.size()<= -1 一定成立):

 int main() {
     string s1, s2 = "abc";
     cout << s1.empty() << endl;
     cout << s2.empty() << endl;
 ​
     cout << s2.size() << endl;
     return 0;
 }

(4)string 的比较: 支持 > < >= <= == !=等所有比较操作,按字典序进行比较。

(5)两个 string 对象相加:

 int main() {
     string s1="hello ",s2="world\n";
     string s3 = s1 + s2;
     string s4 = s1 + "," + s2 + '\n';
     
     cout<<s3<<endl;
 ​
     return 0;
 }

当把 string 对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是 string: string s4 = s1 + “, “; // 正确:把一个 string 对象和有一个字面值相加 string s5 = “hello” +”, “; // 错误:两个运算对象都不是 string string s6 = s1 + “, “ + “world”; // 正确,每个加法运算都有一个运算符是 string string s7 = “hello” + “, “ + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的

3.3 处理 string 对象中的字符

可以将 string 对象当成字符数组来处理:

 int main() {
    string s="hello world";
    for(int i=0;i<s.size();i++){
        cout<<s[i]<<endl;
    }
     return 0;
 }
 int main() {
     string s = "hello world";
     for (char c:s) cout << c << endl;
     for (char &c:s) c = 'a';  //改变原内容需要加&
     for (auto c:s) cout << c << endl;  //auto:编译器自己猜类型
     
     return 0;
 }
3.4取字符串的某一段
 int main() {
     string s1;
     getline(cin,s1);
     cout<<s1.substr(0,5); //参数:(起止位置,长度)(长度省略一直到结尾)
 ​
     return 0;
 }
3.5字符串流

导入库函数 #include <sstream>

把一个字符串初始化成类似cin的东西,得到我们任意想要的格式

 int main() {
     string s;
     getline(cin, s);
     stringstream ssin(s);
 ​
     int a, b;
     string str;
     double c;
 ​
     ssin >> a >> str >> b >> c;
     cout << a << endl << str << endl << b << endl << c << endl;
 ​
     return 0;
 }
3.5字符串最后一位处理
 int main() {
     string s;
     cin>>s;
     cout<<s.back()<<endl;     //s.back()返回最后一位字符
     s.pop_back();             // s.pop_back()删去最后一位字符
 ​
     return 0;
 }

函数

1. 函数基础

一个典型的函数定义包括以下部分:返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体。

1.1 编写函数
 //函数声明
 int func(int n);
 ​
 //函数定义
 int func(int n) {
     int res = 1;
     for (int i = 1; i <= n; i++) {
         res *= i;
     }
 ​
     return res;
 }
 ​
 //调用函数
 int main() {
     int t = func(5);
     cout << t << endl;
 ​
     return 0;
 }
1.2形参和实参

实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,依次类推。形参和实参的类型和个数必须匹配。 fact(“hello”); // 错误:实参类型不正确 fact(); // 错误:实参数量不足 fact(42, 10, 0); // 错误:实参数量过多 fact(3.14); // 正确:该实参能转换成 int 类型,等价于 fact(3); 形参也可以设置默认值,但所有默认值必须是最后几个。当传入的实参个数少于形参个数时,最后没有被传入值的形参会使用默认值。

1.3函数的形参列表

函数的形参列表可以为空,但是不能省略。 void f1() {/ .... /} // 隐式地定义空形参列表 void f2(void) {/ ... /} // 显式地定义空形参列表 形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声 明。即使两个形参的类型一样,也必须把两个类型都写出来: int f3(int v1, v2) {/ ... /} // 错误 int f4(int v1, int v2) {/ ... /} // 正确

1.4局部变量、全局变量与静态变量

局部变量只可以在函数内部使用,全局变量可以在所有函数内使用。当局部 变量与全局变量重名时,会优先使用局部变量。

静态变量:(等价于在函数内部开了一个只有该函数能用的全局变量)

 int output(){
     static int cnt=0;  //静态变量:无论调用函数多少次,都使用同一个变量,初始化只在第一次调用执行
     cnt++;
     cout<<cnt<<"times"<<endl;
 }
 ​
 int main() {
     output();
     output();
     output();
     output();
 ​
     return 0;
 }
2.参数传递
2.1 传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。

 int func(int x){
     x=5;
     return x;
 }
 ​
 int main() {
     int x=10;
     int t=func(x);
     cout<<x<<' '<<t<<endl;   //10 5
     
     return 0;
 }
2.2 传引用参数

当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。

 int func(int &x){
     x=5;
     return x;
 }
 ​
 int main() {
     int x=10;
     int t=func(x);
     cout<<x<<' '<<t<<endl;    //5 5 
 ​
     return 0;
 }
2.3 数组形参

在函数中对数组中的值的修改,会影响函数外面的数组。

一维数组形参的写法: // 尽管形式不同,但这三个 print 函数是等价的 void print(int *a) {/ ... /} void print(int a[]) {/ ... /} void print(int a[10]) {/ ... /}

 void output(int n,int a[3]){   //output(int n,int *a)   output(int n,int a[])
     for(int i=0;i<n;i++){
         cout<<a[i]<<endl;
     }
 }
 ​
 int main() {
     int a[3]={1,2,3};
     output(3,a);
 ​
     return 0;
 }

多维数组形参的写法: // 多维数组中,除了第一维之外,其余维度的大小必须指定 void print(int (*a)[10]) {/ ... /} void print(int a[][10]) {/ ... /}

 void output(int n, int m, int a[][3]) {
     for (int i = 0; i < n; i++) {
         for (int j = 0; j < m; j++) {
             cout << a[i][j] << endl;
         }
     }
 }
 ​
 int main() {
     int a[3][3] = {
             {1, 2, 3},
             {4, 5, 6},
             {7, 8, 9},
     };
     output(3, 3, a);
 ​
     return 0;
 }

3.返回类型和 return 语句

return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方 。 return 语句有两种形式: return; return expression;

3.1有返回值的函数

只要函数的返回类型不是 void,则该函数内的每条 return 语句必须返回一个值。return 语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。

3.2无返回值函数

没有返回值的 return 语句只能用在返回类型是 void 的函数中。返回 void 的函数不要求非得有 return 语句,因为在这类函数的最后一句后面会隐式地执行return。 通常情况下,void 函数如果想在它的中间位置提前退出,可以使用 return 语句。return 的这种用法有点类似于我们用 break 语句退出循环。

 void swap(int &v1, int &v2)
 {
 // 如果两个值相等,则不需要交换,直接退出
     if (v1 == v2)
         return;
 // 如果程序执行到了这里,说明还需要继续完成某些功能
     int tmp = v2;
     v2 = v1;
     v1 = tmp;
 // 此处无须显示的 return 语句
 }
4.函数递归

在一个函数内部,调用函数本身。

 using namespace std;
 ​
 int fact(int n){
     if(n<=1) return 1;
     return n*fact(n-1);
 }
 ​
 int main() {
     int n;
     cin>>n;
 ​
     cout<<fact(n)<<endl;
 ​
     return 0;
 }

类、结构体、指针、引用

1.类与结构体
1.类

类中的变量和函数被统一称为类的成员变量。

private 后面的内容是私有成员变量,在类的外部不能访问;public 后面的内容是公有成员变量,在类的外部可以访问。

 class Person{
 private:
     int age,height;
     double money;
     string books[100];
 ​
 public:
     string name;
     void say(){
         cout<<"i am "<<name<<endl;
     }
 ​
     int get_age(){
         return age;
     }
 ​
     void add_money(double x){
         money+=x;
     }
 ​
 };   //类后面要加分号
 ​
 ​
 int main() {
     Person c;
     c.name="hutao";
     //c.age=18;            //错误!age是私有变量
     c.add_money(1000);
     cout<<c.get_age()<<endl;
 ​
     return 0;
 }

2.结构体

结构体和类的作用是一样的。不同点在于类默认是 private,结构体默认是 public。

 struct Person{
 private:
     int age,height;
     double money;
     string books[100];
 ​
 public:
     string name;
     void say(){
         cout<<"i am "<<name<<endl;
     }
     int set_age(int a){
         age=a;
     }
 ​
     int get_age(){
         return age;
     }
 ​
     void add_money(double x){
         money+=x;
     }
 ​
 } person_a,person_b,persons[100];   //定义完直接声明

(习惯上把只有数据的、函数比较少的定义为结构体;函数麻烦的、多的打包为类)

3.构造函数
 struct Person{
     int age,height;
     double money;
 ​
     Person(){}       //没有参数的构造函数
 ​
     Person(int _age,int _height,double _money){     //构造函数
         age =_age;
         height=_height;
         money=_money;
     }
 //构造函数也可以写成 
 Person(int _age,int _height,double _money) : age(_age),height(_height),money(_money){}
     
 };
 ​
 ​
 int main() {
     Person p1(18,185,100.0);
     Person p2={20,186,200.5};
     cout<<p1.age<<' '<<p2.money<<endl;
 ​
     return 0;
 }
2.指针与引用

指针指向存放变量的值的地址。因此我们可以通过指针来修改变量的值。

 int main() {
     int a = 10;
     int *p = &a;     //这里的*代表变量p是指针类型,可以看作int* p(定义int类型的指针,p为变量名称)
     *p += 5;
     cout << *p << endl;   //求变量p的值,此处*为操作符
 ​
     return 0;
 }

数组名是一种特殊的指针。指针可以做运算:

 int main() {
     int a[5] = {1, 2, 3, 4, 5};
 ​
     for (int i = 0; i < 5; i++) {
         cout << *(a + i) << endl;   //指针+1的长度加的是一个类型的长度
     }
 ​
     return 0;
 }
 int main() {
     int a[5] = {1, 2, 3, 4, 5};
 ​
     int *p = &a[0], *q = &a[2];
     cout << q - p << endl;     //2
 ​
     return 0;
 }

引用和指针类似,相当于给变量起了个别名。

 int main() {
     int a=10;
     int &p=a;    //定义一个变量p,与a同一个地址。
     p+=5;
     cout<<p<<' '<<a<<endl;
 ​
     return 0;
 }
3.链表
 struct Node {
     int val;
     Node *next;
 ​
     Node(int _val) : val(_val), next(NULL) {}
 };
 ​
 ​
 int main() {
     Node node = Node(1);
     Node *p = &node;          //一般习惯定义完放指针里
 //以上两步等价于一步
     Node *p=new Node(1);//没有new代表定义一个Node变量值是1;有new代表定义了一个Node变量返回值是地址
     Node *q = new Node(2);
     Node *o = new Node(3);
 ​
     p->next = q;//调用类里面成员变量时,如果变量是指针(地址)用’->‘;不是指针用变量名+‘.’
     q->next = o;
 ​
     Node *head = p;//说链表的头结点大部分情况下说的是第一个节点的地址,而不是值。
 ​
     for(Node* i=head;i;i=i->next){          //链表的遍历
         cout<<i->val<<endl;
     } 
 ​
     return 0;
 }

STL

1.vector
 #include <vector>

vector 是变长数组,支持随机访问,不支持在任意位置 O(1)插入。为了保证效率,元素的增删一般应该在末尾进行。

 int main() {
     vector<int> a;       //相当于一个长度动态变化的 int 数组
     vector<int> a({1,2,3}); 
     vector<int> b[233];  //相当于第一维长 233,第二维长度动态变化的 int 数组
 ​
     struct rec{
         int x,y;
     };
     vector<rec> c;       //自定义的结构体类型也可以保存在 vector 中
 ​
     a.size();           //返回 vector 的实际长度(包含的元素个数)
     a.empty();          //返回一个bool 类型,表明 vector 是否为空。
     a.clear();          //把 vector 清空。
     
     return 0;
 }

迭代器: 迭代器就像 STL 容器的“指针”,可以用星号“*”操作符解除引用。 一个保存 int 的 vector 的迭代器声明方法为:

     vector<int>::iterator it = a.begin();

vector 的迭代器是“随机访问迭代器”,可以把 vector 的迭代器与一个整数相加减,其行为和指针的移动类似。可以把 vector 的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。

begin/end: begin 函 数 返 回 指 向 vector 中 第 一 个 元 素 的 迭 代 器 。 例 如 a 是 一 个 非 空 的vector,则*a.begin()与 a[0]的作用相同。 所有的容器都可以视作一个“前闭后开”的结构,end 函数返回 vector 的尾部,即 第 n 个 元 素 再 往 后 的 “ 边 界 ” *a.end() 与 a[n] 都 是 越 界 访 问 , 其 中n=a.size()。

 //下面代码都遍历了 vector<int>a,并输出它的所有元素。
 for (int i = 0; i < a.size(); i ++) cout << a[i] << endl;
 for (vector<int>::iterator it = a.begin(); it != a.end(); it ++) cout <<*it << endl;
 for (auto it = a.begin(); it != a.end(); it ++) cout <<*it << endl;//用auto替代
 ​
 for(int x:a) cout<<x<<' ';//范围遍历,与string一样

front/back front 函数返回 vector 的第一个元素,等价于*a.begin() 和 a[0]。 back 函数返回 vector 的最后一个元素,等价于a[a.size() –1]。

 cout<<a.front()<<' '<<a[0]<<endl;
 cout<<a.back()<<' '<<a[a.size()-1]<<endl;

push_back() 和 pop_back() a.push_back(x) 把元素 x 插入到 vector a 的尾部。 a.pop_back() 删除 vector a 的最后一个元素。

2.queue
 #include <queue>

头文件 queue 主要包括循环队列 queue 和优先队列 priority_queue 两个容器。

 queue<int> q;
 struct rec{
     int a,x,y;
 };
 queue<rec> q;//结构体rec的队列
 ​
 priority_queue<int> q;                           // 大根堆
 priority_queue<int, vector<int>, greater<int> q; // 小根堆
 priority_queue<pair<int, int>>q;                 //pair二元组
 ​
 ​
 struct rec {
         int a, b;
 ​
         bool operator<(const rec &t) const {
             return a < t.a;
         }
     };
     priority_queue<rec> q;//定义结构体类型的大根堆,结构体 rec 中必须定义小于号;小根堆定义大于号.

 循环队列 queue:
 queue<int> q;//队头插入,队尾弹出
 q.push(1);//在对头插入一个元素
 q.pop();//弹出队尾元素
 q.front();//返回队头
 q.back();//返回队尾
 ​
 优先队列 priority_queue:
 priority_queue<int> a;
 a.push(1);//插入一个数
 a.top();//取最大值
 a.pop();//删除最大值

队列没有clear,如果要清空队列,重新初始化即可

 q = queue<int>();

3.stack
 #include <stack>
 stack<int> stk;
 ​
 stk.push(1);     //向栈顶插入
 stk.top();       //返回栈顶元素
 stk.pop();       //弹出栈顶元素

4.deque
 #include <deque>

双端队列 deque 是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是 vector 和 queue 的结合。与 vector 相比,deque 在头部增删元素仅需要 O(1)的时间;与 queue 相比,deque 像数组一样支持随机访问。

 deque<int> a;
 ​
 a.begin(),a.end();            //返回 deque 的头/尾迭代器
 a.front(),a.back();           //队头/队尾元素
 a.push_back(1);               //从队尾入队
 a.push_front(1);              //从队头入队
 a.pop_back();                 //从队尾出队
 a.pop_front();                //从队头出队
 a.clear();                    //清空队列

5.set
 #include <set>

头文件 set 主要包括 set 和 multiset 两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set 和 multiset 的内部实现是一棵红黑树,它们支持的函数基本相同。

 set<int> a;         //元素不能重复
 multiset<int> b;    //元素可以重复
 ​
 struct rec {
     int x, y;
 ​
     bool operator<(const rec &t) const {
         return x < t.x;
     }
 };
 set<rec> c;
 a.size();
 a.empty();
 a.clear();

迭代器: set 和 multiset 的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持”++”和--“两个与算术相关的操作。 设 it 是一个迭代器,例如 :

 set<int>::iterator it = a.begin();

若把 it++,则 it 会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在 it 下一名的元素。同理,若把 it--,则 it 将会指向排在“上一个”的元素。

 s.begin();//是指向集合中最小元素的迭代器。
 s.end();//是指向集合中最大元素的下一个位置的迭代器。因此--s.end()是指向集合中最大元素的迭代器。

insert/find:

 s.insert(x); //把一个元素 x 插入到集合 s 中,时间复杂度为 O(logn)。
 s.find(x); //在集合 s 中查找等于 x 的元素,并返回指向该元素的迭代器。若不存在,则返回 s.end()。时间             复杂度为 O(logn)。
 if(s.find(x)==s.end())//来判断x是否存在

lower_bound/upper_bound:

这 两 个 函 数 的 用 法 与 find 类 似 , 但 查 找 的 条 件 略 有 不 同 , 时 间 复 杂 度 为O(logn)。

 s.lower_bound(x);//查找大于等于 x 的元素中最小的一个,并返回指向该元素的迭代器。
 s.upper_bound(x);//查找大于 x 的元素中最小的一个,并返回指向该元素的迭代器。

erase:

 s.erase(it);//从 s 中删除迭代器 it 指向的元素,时间复杂度为O(logn)
 s.erase(x);//从 s 中删除所有等于 x 的元素,时间复杂度为O(k+logn),其中 k 是被删除的元素个数。

count:

 s.count(x);//返回集合 s 中等于 x 的元素个数,时间复杂度为 O(k +logn),其中k 为元素 x 的个数。

6.map
 #include <map>

map 容器是一个键值对 key-value 的映射,其内部实现是一棵以 key 为关键码的红黑树。Map 的 key 和 value 可以是任意类型,其中 key 必须定义小于号运算符。

 map<string,int> a;
 a["hello"]=2;
 cout<<a["hello"]<<endl;
 map<string,vector<int>> a;
 a["hello"]=vector<int>({1,2,3,4});
 cout<<a["hello"][2]<<endl;

size/empty/clear/begin/end 均与 set 类似。

insert/erase:

 map<string,int> a;
 a.insert({"hi",3});       //插入一个二元组
 cout<<a["hi"]<<endl;
 cout << (a.find("hi") == a.end()) << endl;//a.find(x) 在变量名为 a 的 map 中查找 key 为 x 的二元组。

7.bitset
 #include <bitset>
 bitset<1000> a;  //定义一个长度为1000的0、1串,默认为0
 a[0]=1;
 ​
 cout<<a.count();//返回1的个数
 ​
 a.set(3);//把第三位设成1
 a.reset(3);//把第三位设成0

位运算

& 与 0&0=0; 0&1=0; 1&0=0; 1&1=1; | 或 0|0=0; 0|1=1; 1|0=1; 1|1=1; ~ 非 ~0=1; ~1=0; ^ 异或 0^0=0; 1^1=0; 1^0=1; 0^1=1; >> 右移 a>>k(a右移k位) 等价于 a除以2的k次方 << 左移 a<<k(a左移k位) 等价于 a乘以2的k次方

常用操作: (1) 求 x 的第 k 位数字 x >> k & 1 (2) lowbit(x) = x & -x,返回 x 的最后一位 1 等价于a&(~a+1)

常用库函数

 #include <algorithm>
1.reverse 翻转

翻转一个 vector: reverse(a.begin(), a.end());

翻转一个数组,元素存放在下标 1~n: reverse(a + 1, a + 1 + n);

 int a[]={1,2,3,4,5};
 reverse(a,a+5);//取到最后一个元素的下一个

2.unique 去重

前提:相同元素必须挨在一起

返回去重之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。

把一个 vector 去重: int m = unique(a.begin(), a.end()) – a.begin(); 把一个数组去重,元素存放在下标 1~n: int m = unique(a + 1, a + 1 + n) – (a +1);

数组去重处理:

 vector<int> a({1, 1, 2, 2, 3, 3, 4});
 sort(a.begin(), a.end()); // 对向量进行排序
 ​
 a.erase(unique(a.begin(),a.end()),a.end());//去除重复的元素之后剩下的数组
 for (auto x:a) cout << x << ' ';

3.random_shuffle 随机打乱
 #include <algorithm>
 #include <ctime>
 ​
 int main() {
     vector<int> a({1, 2, 3, 4, 5});
 ​
     srand(time(0));//设置种子,通常传入时间作为随机
 ​
     random_shuffle(a.begin(),a.end());
 ​
     for(auto x:a) cout<<x<<' ';
 ​
     return 0;
 }

4.sort排序

对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义 大小比较的函数,或者重载“小于号”运算符。

 vector<int> a({4, 3, 2, 1, 5});
 sort(a.begin(),a.end());
 for(auto x:a) cout<<x<<' ';
 bool cmp(int a,int b){//a是否应该排在b的前面
     return a>b;
 }
 ​
 int main() {
     vector<int> a({4, 3, 2, 1, 5});
 ​
     sort(a.begin(),a.end());//从小到大排
     for(auto x:a) cout<<x<<' ';
 ​
     sort(a.begin(),a.end(),greater<>());//从大到小排
     for(auto x:a) cout<<x<<' ';
 ​
     sort(a.begin(),a.end(),cmp);//自定义排序
     for(auto x:a) cout<<x<<' ';
 ​
     return 0;
 }

比较结构体:

 struct Rec {
     int x, y;
 };//
 ​
 bool cmp(Rec a, Rec b) {    //a是否应该排在b的前面
     return a.x < b.x;
 }
 ​
 int main() {
     Rec a[5];
 ​
     for (int i = 0; i < 5; i++) {
         a[i].x = -i;
         a[i].y = i;
     }
 ​
     for (int i = 0; i < 5; i++) printf("(%d,%d)", a[i].x, a[i].y);
     cout<<endl;
 ​
     sort(a, a + 5,cmp);
 ​
     for (int i = 0; i < 5; i++) printf("(%d,%d)", a[i].x, a[i].y);
 ​
     return 0;
 }
5.lower_bound/upper_bound 二分

lower_bound 的第三个参数传入一个元素 x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于 x 的元素的位置的迭代器(指针)。 upper_bound 的用法和 lower_bound 大致相同,唯一的区别是查找第一个大于x 的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。

 int a[] = {1, 2, 4, 5, 6};
 ​
 int *p = lower_bound(a, a + 6, 3);
 cout << *p << endl;     //返回值
 ​
 int t = lower_bound(a, a + 6, 3)-a;
 cout << t << endl;      //返回下标
 vector<int> a{1,2,3,4,5,6};
 ​
 int t= lower_bound(a.begin(),a.end(),3)-a.begin();
 cout<<a[t]<<endl;
6.next_permutation()

next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。

当当前序列不存在下一个排列时,函数返回false,否则返回true;

 vector<vector<int>> res;
 do{
     res.push_back(nums);
 }while(next_permutation(nums.begin(),nums.end()));
        
 return res;

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值