?#include <iostream> ?? ?using namespace std; ?? ?int main(){ ? ? ?cout << "Hello World" << endl; ? ? ? ? ? ?return 0; ?}
变量类型:
类型 | 关键字 | 大小 |
---|---|---|
布尔型 | bool | 1btye |
字符型 | char | 1btye |
整型 | int | 4btye |
浮点型(单精度) | float | 4btye |
双浮点型(双精度) | double | 8btye |
变量必须先定义,才可以使用。不能重名。
?int main() { ? ? ?int a, b = 2, c = b; ? ? ?float d = 1.5, f = 1.23e2; ? ? ?bool g = true; ? ? ?char j = 'a'; ?? ? ? ?return 0; ?}
?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
?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; ?}
?int main() { ? ? ?float x = 123.12; ? ? ?int y = (int) x; ? ? ?cout << x << ' ' << y << endl;//向下取整 ?? ? ? ?return 0; ?}
隐式转换:低精度转为高精度
?#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; ?}
当条件成立时,执行某些语句;否则执行另一些语句。else语句可以省略
?int main() { ? ? ?int score; ? ? ?cin >> score; ? ? ?if (score >= 60) { ? ? ? ? ?cout << "及格" << endl; ? ? } else { ? ? ? ? ?cout << "不及格" << endl; ? ? } ?? ? ? ?return 0; ?}
?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; ?}
与 && 或 || 非 !
?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; ?}
?int main() { ? ? ?int n=10,i=1,sum=0; ? ? ?do{ ? ? ? ? ?sum+=i; ? ? ? ? ?i++; ? ? }while(i<=10); ?? ? ? ?printf("%d",sum); ? ? ?return 0; ?}
for(初始化语句;条件语句;表达式)语句;
?int main() { ? ? ?int i,sum; ? ? ?for(i=1,sum=0;i<=100;i++){ ? ? ? ? ?sum+=i; ? ? } ? ? ?printf("%d",sum); ? ? ?return 0; ?}
?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; ?}
?int main() { ? ? ?int i,sum; ? ? ?for(i=1,sum=0;i<=100;i++){ ? ? ? ? ?if(sum>2000){ ? ? ? ? ? ? ?break; ? ? ? ? } ? ? ? ? ?sum+=i; ? ? } ? ? ?printf("%d",sum); ? ? ?return 0; ?}
?int main() { ? ? ?int a[10],b[20]; ? ? ?float f[33]; ? ? ?double d[123]; ? ? ?char c[21]; ?? ? ? ?return 0; ?}
?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。
?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; ?}
?//头文件 ?#include <algorithm> ?//使用方法 ?reverse(a, a+n);//n为数组中的元素个数 ?? ?reverse(a,a+n);//翻转整个数组 ?reverse(a,a+k);//翻转前k个元素 ?reverse(a+k,a+n);//反转后n-k个元素
?#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; ?}
?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; ?}
?#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; ?}
?#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; ?}
每个常用字符都对应一个-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; ?}
字符串就是字符数组加上结束符’\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; ?}
?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; ?}
下面几个函数需要引入头文件:
#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 开始的字符数组。
?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; ?}
可变长的字符序列,比字符数组更加好用。需要引入头文件: #include <string>
?int main() { ? ? ?string s1; ? ? ? ? //默认初始化,s1是一个空字符串 ? ? ?string s2=s1; ? ? ?//s2是s1的副本 ? ? ?string s3="hello"; //s3是该字符串字面值的副本 ? ? ?string s4(10,'c'); //s4的内容是cccccccccc ?? ? ? ?return 0; ?}
(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; // 错误:不能把字面值直接相加,运算是从左到右进行的
可以将 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; ?}
?int main() { ? ? ?string s1; ? ? ?getline(cin,s1); ? ? ?cout<<s1.substr(0,5); //参数:(起止位置,长度)(长度省略一直到结尾) ?? ? ? ?return 0; ?}
导入库函数 #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; ?}
?int main() { ? ? ?string s; ? ? ?cin>>s; ? ? ?cout<<s.back()<<endl; ? ? //s.back()返回最后一位字符 ? ? ?s.pop_back(); ? ? ? ? ? ? // s.pop_back()删去最后一位字符 ?? ? ? ?return 0; ?}
一个典型的函数定义包括以下部分:返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体。
?//函数声明 ?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; ?}
实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,依次类推。形参和实参的类型和个数必须匹配。 fact(“hello”); // 错误:实参类型不正确 fact(); // 错误:实参数量不足 fact(42, 10, 0); // 错误:实参数量过多 fact(3.14); // 正确:该实参能转换成 int 类型,等价于 fact(3); 形参也可以设置默认值,但所有默认值必须是最后几个。当传入的实参个数少于形参个数时,最后没有被传入值的形参会使用默认值。
函数的形参列表可以为空,但是不能省略。 void f1() {/ .... /} // 隐式地定义空形参列表 void f2(void) {/ ... /} // 显式地定义空形参列表 形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声 明。即使两个形参的类型一样,也必须把两个类型都写出来: int f3(int v1, v2) {/ ... /} // 错误 int f4(int v1, int v2) {/ ... /} // 正确
局部变量只可以在函数内部使用,全局变量可以在所有函数内使用。当局部 变量与全局变量重名时,会优先使用局部变量。
静态变量:(等价于在函数内部开了一个只有该函数能用的全局变量)
?int output(){ ? ? ?static int cnt=0; ?//静态变量:无论调用函数多少次,都使用同一个变量,初始化只在第一次调用执行 ? ? ?cnt++; ? ? ?cout<<cnt<<"times"<<endl; ?} ?? ?int main() { ? ? ?output(); ? ? ?output(); ? ? ?output(); ? ? ?output(); ?? ? ? ?return 0; ?}
当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。
?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; ?}
当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。
?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; ?}
在函数中对数组中的值的修改,会影响函数外面的数组。
一维数组形参的写法: // 尽管形式不同,但这三个 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; ?}
return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方 。 return 语句有两种形式: return; return expression;
只要函数的返回类型不是 void,则该函数内的每条 return 语句必须返回一个值。return 语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。
没有返回值的 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 语句 ?}
在一个函数内部,调用函数本身。
?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; ?}
类中的变量和函数被统一称为类的成员变量。
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; ?}
结构体和类的作用是一样的。不同点在于类默认是 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]; ? //定义完直接声明
(习惯上把只有数据的、函数比较少的定义为结构体;函数麻烦的、多的打包为类)
?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; ?}
指针指向存放变量的值的地址。因此我们可以通过指针来修改变量的值。
?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; ?}
?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; ?}
?#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 的最后一个元素。
?#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>();
?#include <stack>
?stack<int> stk; ?? ?stk.push(1); ? ? //向栈顶插入 ?stk.top(); ? ? ? //返回栈顶元素 ?stk.pop(); ? ? ? //弹出栈顶元素
?#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(); ? ? ? ? ? ? ? ? ? ?//清空队列
?#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 的个数。
?#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 的二元组。
?#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>
翻转一个 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);//取到最后一个元素的下一个
前提:相同元素必须挨在一起
返回去重之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。
把一个 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 << ' ';
?#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; ?}
对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义 大小比较的函数,或者重载“小于号”运算符。
?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; ?}
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;
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;