入门 打开编译软件,出现的第一个程序肯定是Hello Word!
#include <iostream> using namespace std; int main () { cout << "Hello world!" << endl; return 0 ; }
语法基础
变量的定义: 变量必须先定义,才可以使用。不能重名。
变量定义的方式:
#include <iostream> using namespace std;int main () { int a=5 ; int c=a,d=10 /2 ; return 0 ; }
输入输出 整数的输入输出:
#include <iostream> using namespace std;int main () { int a,b; cin>>a>>b; cout<<a+b<<endl; return 0 ; }
字符串的输入输出:
#include <iostream> #include <string> using namespace std;int main () { string str; cin>>str; cout<<str; return 0 ; }
输入输出多个不同类型的变量:
#include <iostream> #include <string> using namespace std;int main () { int a,b; string str; cin>>a; cin>>b>>str; cout<<str<<" !!! " <<a+b<<endl; return 0 ; }
表达式 整数的加减乘除四则运算:
#include <iostream> #include <string> using namespace std;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; return 0 ; }
浮点数(小数)的运算:
#include <iostream> #include <string> using namespace std;int main () { float x=1.5 ,y=3.2 ; cout<<x*y<<' ' <<x+y<<endl; cout<<x-y<<' ' <<x/y<<endl; return 0 ; }
整型变量的自增、自减:
#include <iostream> #include <string> using namespace std;int main () { int a=1 ; int b=a++; cout<<a<<' ' <<b<<endl; int c=++a; cout<<a<<' ' <<c<<endl; return 0 ; }
变量的类型转换:
#include <iostream> #include <string> using namespace std;int main () { float x=123.12 ; int y=(int )x; cout<<x<<' ' <<y<<endl; return 0 ; }
Printf语句与判断结构 printf输出格式 #include <iostream> #include <cstdio> using namespace std;int main () { printf ("Hello World!" ); return 0 ; }
Int、float、double、char等类型的输出格式: (1)Int:%d (2)Float: %f, 默认保留6位小数 (3)Double: %lf, 默认保留6位小数 (4)Char: %c, 回车也是一个字符,用’\n’表示 (5)Float, double等输出保留若干位小数时用:%.4f, %3lf
#include <iostream> #include <cstdio> using namespace std;int main () { int a=3 ; float b=3.12345678 ; double c=3.12345678 ; printf ("%5d\n" ,a); printf ("%8.3f\n" ,b); printf ("%-8.3f\n" ,b); printf ("%08.3f\n" ,b); printf ("%7.3f\n" ,c); return 0 ; }
if 语句
1.基本if-else语句,当条件成立时,执行某些语句;否则执行另一些语句。 2.Else 语句可以省略。 3.当只有一条语句时,大括号可以省略。常用比较运算符 (1) 大于 > (2) 小于 < (3) 大于等于 >= (4) 小于等于 <= (5) 等于 == (6) 不等于 !=
判断闰年,闰年输出Yes,反之输出No
#include <iostream> #include <cstdio> using namespace std;int main () { int year; cin>>year; if (year%400 ==0 || (year%4 ==0 && year%100 )) puts ("Yes" ); else puts ("No" ); return 0 ; }
循环结构 while循环
可以简单理解为循环版的if语句。If语句是判断一次,如果条件成立,则执行后面的语句;while是每次判断,如果成立,则执行循环体中的语句,否则停止。
求斐波那契数列的第n项。f(1)=1, f(2)=1, f(3)=2, f(n)=f(n-1) + f(n-2)。
#include <iostream> #include <cstdio> using namespace std;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 ; }
死循环:循环永久执行,无法结束。我们要避免写出死循环。
do while循环
do while循环不常用。 do while语句与while语句非常相似。唯一的区别是,do while语句限制性循环体后检查条件。不管条件的值如何,我们都要至少执行一次循环。
#include <iostream> #include <cstdio> using namespace std;int main () { int x=1 ; while (x<1 ) { cout<<"x!" <<endl; x++; } int y=1 ; do { cout<<"y!" <<endl; }while (y<1 ); return 0 ; }
for 循环
基本思想:把控制循环次数的变量从循环体中剥离。 for (init-statement : condition: expression) { statement } init-statement可以是声明语句、表达式、空语句,一般用来初始化循环变量; condition 是条件表达式,和while中的条件表达式作用一样;可以为空,空语句表示true expression 一般负责修改循环变量,可以为空 init-statement可以定义多个变量,expression也可以修改多个变量。
#include <iostream> #include <cstdio> using namespace std;int main () { for (int i=0 ;i<10 ;++i) cout<<i<<endl; return 0 ; }
跳转语句
break可以提前从循环中退出,一般与if语句搭配。
判断一个大于1的数是否是质数。
#include <iostream> #include <cstdio> using namespace std;int main () { int n; cin>>n; bool is_prime=true ; for (int i=2 ;i<n;i++) if (n%i==0 ) { is_prime=false ; break ; } if (is_prime) cout<<"yes" <<endl; else cout<<"no" <<endl; return 0 ; }
continue可以直接跳到当前循环体的结尾。作用与if语句类似。
求1~100中所有偶数的和。
#include <iostream> #include <cstdio> using namespace std;int main () { int sum=0 ; for (int i=1 ;i<=100 ;i++) { if (i%2 ) continue ; sum+=i; } cout<<sum<<endl; return 0 ; }
打印1~100中的所有质数
#include <iostream> #include <cstdio> using namespace std;int main () { for (int i=2 ;i<=100 ;i++) { bool is_prime=true ; for (int j=2 ;j<i;j++) { if (i%j==0 ) { is_prime=false ; break ; } } if (is_prime) cout<<i<<endl; } return 0 ; }
输入一个n,打印n阶菱形。n是奇数。
#include <iostream> #include <cstdio> using namespace std;int main () { int n; cin>>n; int cx=n/2 ,cy=n/2 ; for (int i=0 ;i<n;i++) { for (int j=0 ;j<n;j++) if (abs (i-cx)+abs (j-cy)<=n/2 ) cout<<"*" ; else cout<<' ' ; cout<<endl; } return 0 ; }
数组
数组的定义方式和变量类似。
#include <iostream> #include <cstdio> using namespace std;int main () { int a[10 ],b[20 ]; float f[33 ]; double d[123 ]; char c[21 ]; return 0 ; }
数组的初始化,在main函数内部,未初始化的数组中的元素是随机的。
#include <iostream> #include <cstdio> using namespace std;int main () { int a[3 ]={0 ,1 ,2 }; int b[]={0 ,1 ,1 }; int c[5 ]={0 ,1 ,2 }; char d[3 ]={'a' ,'b' ,'c' }; return 0 ; }
访问数组元素,通过下标访问数组。
#include <iostream> #include <cstdio> using namespace std;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 ; }
使用数组实现求斐波那契数列的第N项。
#include <iostream> #include <cstdio> using namespace std;int main () { int n; int f[100 ]; cin>>n; f[0 ]=0 ,f[1 ]=1 ; for (int i=2 ;i<=n;i++) f[i]=f[i-1 ]+f[i-2 ]; cout<<f[n]<<endl; return 0 ; }
输入一个n,再输入n个整数。将这个数组顺时针旋转k(k <= n)次,最后将结果输出。
#include <iostream> #include <algorithm> using namespace std;int main () { int n,k; int a[100 ]; cin>>n>>k; for (int i=0 ;i<n;i++) cin>>a[i]; reverse (a,a+k); reverse (a+k,a+n); reverse (a,a+n); for (int i=0 ;i<n;i++) cout<<a[i]<<' ' ; cout<<endl; return 0 ; }
计算2的N次方。N <= 10000
#include <iostream> #include <algorithm> using namespace std;int main () { int a[10000 ],size=1 ,n; a[0 ]=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++]=t; } for (int i=size-1 ;~i;i--) cout<<a[i]; cout<<endl; return 0 ; }
多维数组:多维数组就是数组的数组。 Int a[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组。 Int arr[10][20][30] = {0}; /*** 将所有元素初始化为0 ,大小为10的数组,它的每个元素是含有4个整数的数组, 这些数组的元素是含有30个整数的数组 ***/
#include <iostream> #include <algorithm> using namespace std;int main () { int b[3 ][4 ]={ {0 ,1 ,2 ,3 }, {4 ,5 ,6 ,7 }, {8 ,9 ,10 ,11 } }; return 0 ; }
输入一个n行m列的矩阵,从左上角开始将其按回字形的顺序顺时针打印出来。
#include <iostream> #include <algorithm> using namespace std;int main () { int n,m; int arr[50 ][50 ]; cin>>n>>m; for (int i=0 ;i<n;i++) for (int j=0 ;j<m;j++) cin>>arr[i][j]; bool st[50 ][50 ]={false }; int dx[4 ]={-1 ,0 ,1 ,0 },dy[4 ]={0 ,1 ,0 ,-1 }; int d=1 ,x=0 ,y=0 ; for (int i=0 ;i<n*m;i++) { int a=x+dx[d],b=y+dy[d]; if (a<0 || a>=n || b<0 || b>=m || st[a][b]) { d=(d+1 )%4 ; a=x+dx[d],b=y+dy[d]; } cout<<arr[x][y]<<' ' ; st[x][y]=true ; x=a,y=b; } cout<<endl; return 0 ; }
字符串 字符与整数的联系——ASCII码
每个常用字符都对应一个-128~127的数字,二者之间可以相互转化
#include <iostream> using namespace std;int main () { char c='a' ; cout<<(int )c<<endl; int a=66 ; cout<<(char )a<<endl; return 0 ; }
常用ASCII值:’A’-‘Z’ 是65~90,’a’-‘z’是97-122,’0’-‘9’是48-57。字符可以参与运算,运算时会将其当做整数
#include <iostream> using namespace std;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 ; }
输入一行字符,统计出其中数字字符的个数,以及字母字符的个数。
#include <iostream> using namespace std;int main () { int n=0 ,c=0 ; string s; cin>>s; for (int i=0 ;i<s.size ();i++) { if (s[i]>='0' &&s[i]<='9' ) n++; if ((s[i]>='a' &&s[i]<='z' )||(s[i]>='A' &&s[i]<='Z' )) c++; } cout<<n<<' ' <<c<<endl; return 0 ; }
字符数组
字符串就是字符数组加上结束符’\0’。可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个’\0’字符,因此字符数组的长度至少要比字符串的长度多1!
#include <iostream> using namespace std;int main () { char a1[]={'C' ,'+' ,'+' }; char a2[]={'C' ,'+' ,'+' ,'\0' }; char a3[]="C++" ; char a4[6 ]="Daniel" ; return 0 ; }
字符数组的输入输出
#include <iostream> using namespace std;int main () { char str[100 ]; cin>>str; cout<<str<<endl; printf ("%s\n" ,str); return 0 ; }
读入一行字符串,包括空格
#include <iostream> using namespace std;int main () { char str[100 ]; gets (str); cout<<str<<endl; return 0 ; }
字符数组的常用操作
下面几个函数需要引入头文件: #include <string.h> (1)strlen(str):求字符串的长度 (2)strcmp(a, b):比较两个字符串的大小,a < b 返回-1,a == b 返回0,a > b返回1。这里的比较方式是字典序! (3)strcpy(a, b):将字符串b复制给从a开始的字符数组。
#include <iostream> #include <string.h> using namespace std;int main () { char a[100 ]="hello world!" ,b[100 ]; cout<<strlen (a)<<endl; strcpy (b,a); cout<<strcmp (a,b)<<endl; return 0 ; }
遍历字符数组中的字符
#include <iostream> #include <string.h> using namespace std;int main () { char a[100 ]="hello world!" ; for (int i=0 ;i<strlen (a);i++) cout<<a[i]<<endl; return 0 ; }
标准库类型 string 可变长的字符序列,比字符数组更加好用。需要引头文 件:#include <string>
定义和初始化
#include <iostream> #include <string> using namespace std;int main () { string s1; string s2=s1; string s3="hiya" ; string s4 (10 ,'c' ) ; return 0 ; }
string的读写
#include <iostream> #include <string> using namespace std;int main () { string s1,s2; cin>>s1>>s2; cout<<s1<<s2<<endl; return 0 ; }
不能用printf直接输出string,需要写成:printf(“%s”, s.c_str()) 使用getline读取一整行
#include <iostream> #include <string> using namespace std;int main () { string s; getline (cin,s); cout<<s<<endl; return 0 ; }
string的empty和size操作(注意size是无符号整数,因此 s.size() <= -1一定成立)
#include <iostream> #include <string> using namespace std;int main () { string s1,s2="abc" ; cout<<s1.empty ()<<endl; cout<<s2.empty ()<<endl; cout<<s2.size ()<<endl; return 0 ; }
string 的比较:支持 > < >= <= == !=等所有比较操作,按字典序进行比较。 为string对象赋值: string s1(10, ‘c’), s2; // s1的内容是 cccccccccc; //s2是一个空字符串 s1 = s2; // 赋值:用s2的副本替换s1的副本 // 此时s1和s2都是空字符串 两个string对象相加: string s1 = “hello, ”, s2 = “world\n”; string s3 = s1 + s2; // s3的内容是 hello, world\n s1 += s2; // s1 = s1 + s2 字面值和string对象相加:做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来: string s1 = “hello”, s2 = “world”; // 在s1和s2中都没有标点符号 string s3 = s1 + “, “ + s2 + ‘\n’; 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string: string s4 = s1 + “, “; // 正确:把一个string对象和有一个字面值相加 string s5 = “hello” +”, “; // 错误:两个运算对象都不是string string s6 = s1 + “, “ + “world”; // 正确,每个加法运算都有一个运算符是string string s7 = “hello” + “, “ + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的。
处理string对象中的字符,可以将string对象当成字符数组来处理
#include <iostream> #include <string> using namespace std;int main () { string s="hello word" ; for (int i=0 ;i<s.size ();i++) cout<<s[i]<<endl; return 0 ; }
使用基于范围的for语句
#include <iostream> #include <string> using namespace std;int main () { string s="hello word" ; for (char c : s) cout<<c<<endl; for (char &c : s) c='a' ; cout<<s<<endl; return 0 ; }
函数 函数基础
一个典型的函数定义包括以下部分:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。
int fact (int val) { int ret = 1 ; while (val > 1 ) ret *= val -- ; return ret; }
函数名字是fact,它作用于一个整型参数,返回一个整型值。return语句负责结束fact并返回ret的值。 调用函数
int main () { int j = fact (5 ); cout << "5! is " << j << 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) {/ … */} // 正确
函数返回类型
大多数类型都能用作函数的返回类型。一种特殊的返回类型是void,它表示函数不返回任何值。函数的返回类型不能是数组类型或函数类型,但可以是指向数组或者函数的指针。
局部变量、全局变量与静态变量
局部变量只可以在函数内部使用,全局变量可以在所有函数内使用。当局部变量与全局变量重名时,会优先使用局部变量。
参数传递
传值参数:当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。
#include <iostream> #include <string> using namespace std;int f (int x) { x=5 ; } int main () { int x=10 ; f (x); cout<<x<<endl; return 0 ; }
传引用参数:当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。
#include <iostream> #include <string> using namespace std;int f (int &x) { x=5 ; } int main () { int x=10 ; f (x); cout<<x<<endl; return 0 ; }
数组形参:在函数中对数组中的值的修改,会影响函数外面的数组。 一维数组形参的写法: // 尽管形式不同,但这三个print函数是等价的 void print(int a) {/ … /} void print(int a[]) {/ … /} void print(int a[10]) {/ … */}
#include <iostream> #include <string> using namespace std;int print (int a[]) { for (int i=0 ;i<10 ;i++) cout<<a[i]<<endl; } int main () { int a[10 ]; for (int i=0 ;i<10 ;i++) a[i]=i; print (a); return 0 ; }
多维数组形参的写法:多维数组中,除了第一维之外,其余维度的大小必须指定 void print(int (a)[10]) {/ … /} void print(int a[][10]) {/ … */}
#include <iostream> #include <string> using namespace std;int print (int a[][10 ]) { for (int i=0 ;i<10 ;i++) { for (int j=0 ;j<10 ;j++) cout<<a[i][j]<<' ' ; cout<<endl; } } int main () { int a[10 ][10 ]; for (int i=0 ;i<10 ;i++) for (int j=0 ;j<10 ;j++) a[i][j]=j; print (a); return 0 ; }
返回类型和return语句:return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。return语句有两种形式: return; return expression;
无返回值函数:没有返回值的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; }
有返回值的函数:只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。
#include <iostream> using namespace std;int max (int x,int y) { if (x>y) return x; return y; } int main () { int x,y; cin>>x>>y; cout<<max (x,y)<<endl; return 0 ; }
函数递归:在一个函数内部,也可以调用函数本身。
#include <iostream> 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后面的内容是公有成员变量,在类的外部可以访问。
#include <iostream> using namespace std;const int N = 1000010 ;class Person { private : int age, height; double money; string books[100 ]; public : string name; void say () { cout << "I'm " << 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 ]; int main () { Person c; c.name = "Edviv" ; c.age = 18 ; c.set_age (18 ); c.add_money (100 ); c.say (); cout << c.get_age () << endl; return 0 ; }
结构体和类的作用是一样的。不同点在于类默认是private,结构体默认是public。
指针和引用 指针指向存放变量的值的地址。因此我们可以通过指针来修改变量的值
#include <iostream> using namespace std;int main () { int a=10 ; int *p=&a; *p+=5 ; cout<<*p<<endl; return 0 ; }
数组名是一种特殊的指针,指针可以做运算
#include <iostream> using namespace std;int main () { int a[5 ]={1 ,2 ,3 ,4 ,5 }; for (int i=0 ;i<5 ;i++) cout<<*(a+i)<<endl; return 0 ; }
引用和指针类似,相当于给变量起了个别名
#include <iostream> using namespace std;int main () { int a=10 ; int &p=a; p+=5 ; cout<<p<<endl; return 0 ; }
单链表
#include <iostream> #include <string.h> using namespace std;const int N=10000 ;struct Node { int val; Node *next; }*head; int main () { for (int i=1 ;i<=5 ;i++) { Node *p=new Node (); p->val=i; p->next=head; head=p; } for (Node *p=head;p;p=p->next) cout<<p->val<<' ' ; cout<<endl; return 0 ; }
STL vector vector是变长数组,支持随机访问,不支持在任意位置 O(1) 插入。为了保证效率,元素的增删一般应该在末尾进行。
声明: #include <vector> 头文件 vector<int > a; 相当于一个长度动态变化的int 数组 vector<int > b[233 ]; 相当于第一维长233 ,第二位长度动态变化的int 数组 struct rec {…}; vector<rec> c; 自定义的结构体类型也可以保存在vector中
size/empty
size函数返回vector的实际长度(包含的元素个数),empty函数返回一个bool类型,表明vector是否为空。二者的时间复杂度都是 O(1)。 所有的STL容器都支持这两个方法,含义也相同,之后我们就不再重复给出。
clear
clear函数把vector清空。
迭代器
迭代器就像STL容器的“指针”,可以用星号“*”操作符解除引用。一个保存int的
vector的迭代器声明方法:vector<int>::iterator it;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;
front/back
front函数返回vector的第一个元素,等价于*a.begin() 和 a[0]。
back函数返回vector的最后一个元素,等价于*==a.end() 和 a[a.size() – 1]。
push_back() 和 pop_back()
a.push_back(x) 把元素x插入到vector a的尾部。
b.pop_back() 删除vector a的最后一个元素。
queue #include <queue>头文件queue主要包括循环队列queue和优先队列
priority_queue两个容器。
声明: queue<int > q; struct rec {…}; queue<rec> q; priority_queue<int > q; priority_queue<int , vector<int >, greater<int > q; priority_queue<pair<int , int >>q;
循环队列 queue
push 从队尾插入
pop 从队头弹出
front 返回队头元素
back 返回队尾元素
优先队列 priority_queue
push 把元素插入堆
pop 删除堆顶元素
top 查询堆顶元素(最大值)
stack #include <stack>
头文件stack包含栈。声明和前面的容器类似。
push 向栈顶插入
pop 弹出栈顶元素
deque #include <deque>
双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像
是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间;
与queue相比,deque像数组一样支持随机访问。
[] 随机访问
begin/end,返回deque的头/尾迭代器
front/back 队头/队尾元素
push_back 从队尾入队
push_front 从队头入队
pop_back 从队尾出队
pop_front 从队头出队
clear 清空队列
set #include <set>
头文件set主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,
即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部
实现是一棵红黑树,它们支持的函数基本相同。
声明: set<int > s; struct rec {…}; set<rec> s; multiset<double > s; size/empty/clear 与vector类似
迭代器
set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)
解除引用,仅支持”++”和--“两个与算术相关的操作。
设it是一个迭代器,例如set<int>::iterator it;
若把it++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排
序的结果中,排在it下一名的元素。同理,若把it--,则it将会指向排在“上一个”
的元素。
begin/end
返回集合的首、尾迭代器,时间复杂度均为O(1)。
s.begin() 是指向集合中最小元素的迭代器。
s.end() 是指向集合中最大元素的下一个位置的迭代器。
换言之,就像vector一样,是一个“前闭后开”的形式。
因此--s.end()是指向集合中最大元素的迭代器。
insert
s.insert(x)把一个元素x插入到集合s中,时间复杂度
为 O(logn)。
在set中,若元素已存在,则不会重复插入该元素,对集
合的状态无影响。
find
s.find(x) 在集合s中查找等于x的元素,并返回指向该
元素的迭代器。若不存在,则返回s.end()。时间复杂
度为 O(logn)。
lower_bound/upper_bound
这两个函数的用法与find类似,但查找的条件略有不同,
时间复杂度为 O(logn)。
s.lower_bound(x) 查找大于等于x的元素中最小的一个,
并返回指向该元素的迭代器。
s.upper_bound(x) 查找大于x的元素中最小的一个,并
返回指向该元素的迭代器。
erase
设it是一个迭代器,s.erase(it) 从s中删除迭代器it
指向的元素,时间复杂度为O(logn)
设x是一个元素,s.erase(x) 从s中删除所有等于x的元
素,时间复杂度为O(k+logn),其中k是被删除的元素个
数。
count
s.count(x) 返回集合s中等于x的元素个数,时间复杂
度为 O(k +logn),其中k为元素x的个数。
map #include <map>
map容器是一个键值对key-value的映射,其内部实现是
一棵以key为关键码的红黑树。Map的key和value可以是
任意类型,其中key必须定义小于号运算符。
声明: map<key_type, value_type> name; 例如: map<long , long , bool > vis; map<string, int > hash; map<pair<int , int >, vector<int >> test; size/empty/clear/begin/end均与set类似。 Insert/erase 与set类似,但其参数均是pair<key_type, value_type>。
find
h.find(x) 在变量名为h的map中查找key为x的二元组。
[]操作符:
h[key] 返回key映射的value的引用,时间复杂度为
O(logn)。
[]操作符是map最吸引人的地方。我们可以很方便地通过
h[key]来得到key对应的value,还可以对h[key]进行赋值
操作,改变key对应的value。
位运算与常用库函数 位运算 & 与 | 或 ~ 非 ^ 异或 >> 右移 << 左移 常用操作: (1 )求x的第k位数字 x >> k & 1 (2 )lowbit (x) = x & -x,返回x的最后一位1
常用库函数 reverse 翻转
翻转一个vector:reverse(a.begin(), a.end());
翻转一个数组,元素存放在下标1~n:
reverse(a + 1, a + 1 + n);
unique 去重
返回去重之后的尾迭代器(或指针),仍然为前闭后开,
即这个迭代器是去重之后末尾元素的下一个位置。该函
数常用于离散化,利用迭代器(或指针)的减法,可计
算出去重后的元素个数。
把一个vector去重:
int m =unique(a.begin(), a.end()) – a.begin();
把一个数组去重,元素存放在下标1~n:
int m = unique(a + 1, a + 1 + n) – (a + 1);
random_shuffle 随机打乱
用法与reverse相同
sort
对两个迭代器(或指针)指定的部分进行快速排序。可
以在第三个参数传入定义大小比较的函数,或者重载“小
于号”运算符。
把一个int数组(元素存放在下标1~n)从大到小排序,
传入比较函数:
int a[MAX_SIZE];
bool cmp(int a, int b) {return a > b; }
sort(a + 1, a + 1 + n, cmp);
把自定义的结构体vector排序,重载“小于号”运算符:
struct rec { int id, x, y; }vector<rec> a; bool operator <(const rec &a, const rec &b) { return a.x < b.x || a.x == b.x && a.y < b.y; } sort (a.begin (), a.end ());
lower_bound/upper_bound 二分
lower_bound 的第三个参数传入一个元素x,在两个迭
代器(指针)指定的部分上执行二分查找,返回指向第
一个大于等于x的元素的位置的迭代器(指针)
upper_bound 的用法和lower_bound大致相同,唯一的
区别是查找第一个大于x的元素。当然,两个迭代器(
指针)指定的部分应该是提前排好序的。
在有序int数组(元素存放在下标1~n)中查找大于等于
x的最小整数的下标:
int I = lower_bound(a + 1, a + 1 + n,. x) – a;
在有序vector<int> 中查找小于等于x的最大整数(假
设一定存在):
int y = *--upper_bound(a.begin(), a.end(), x);