一、《语法基础课》
万能头文件: bits/stdc++.h
字符的读入:
scanf("%c%c", &a, &b); // 会把空格读入cin >> a >> b; // 会忽略中间的空格(1个或多个)
max数学表达式:max = (a + b + |a − b|) / 2
swap函数:
在新c++编译器中,iostream含有swap函数,可交换基础类型,如swap(int x, int y),而不必swap(int &x, int &y)
旧一点的版本需要导入#include
#include
逗号运算符:C++的逗号运算符对逗号前后的表达式进行运算,然后舍弃前一个表达式的返回值,仅仅返回最后一个表达式的返回值
if (表达式1, 表达式2, 表达式3) {...}等价于表达式1;表达式2;if (表达式3) {...}if(cin >> x && x > 0) {...}if(cin >> x, x > 0) {...} // "cin >> x"仅做执行,然后抛弃其返回值
浮点数比较问题:C++中,表达式并不成立。由于浮点数精度损失,应该用
去判断,eps一般取10−6。
cstring一些函数用法:
memset赋值是按字节赋值,因此只有赋予-1和0时才与预期一致。其最后一个参数的单位是Byte
memset(arr, 0, n sizeof(int))
memset(arr, -1, n sizeof(int))
sizeof 可不加括号,即可这样使用sizeof a,其返回单位是Byte
memcpy用于拷贝数组,格式为memcpy(dest,src,sizeof(src))
数组初始化的坑:在函数内定义的数组不会自动初始化为0,都是随机数,例如main函数里。而在函数外定义的数组会自动初始化为0。
读取字符串的方法:
char s[N];scanf("%s", s); // 不能读取含空格、换行符的字符串gets(s); // 能读取含空格的字符串,同时自动去掉换行符\nfgets(s, N, stdin); // 能读取含空格的字符串,但不会去掉换行符\n。【注意】
#include <string>string str;cin >> str; // 不能读取含空格、换行符的字符串getline(cin, str); // 能读取含空格的字符串,同时自动去掉换行符\n
字符串操作:
#include <cstring> // 或<string.h>char a[N], b[N];strlen(a); // O(N)复杂度,使用前最好用变量保存字符串长度。统计的长度不包括`\0`strcat(a, b); // 把字符串b拼接到a之后,拼接后的字符串保存在a中strcmp(a, b); // 根据字典排序比较字符串strcpy(b, a); // 把字符串a的内容拷贝到字符串bfor (int i = 0; str[i]; i++) {...} // 遍历字符串
string str;string s(5, 'a'); // 构造重复字符的字符串str.empty(); // 判空str.size(); // 长度,与stelen()不同的是,这个复杂度是O(1),不用额外的变量保存str.c_str(); // 转成char数组,此时才可用printf输出str.substr(begin, length); // 子串str.pop_back(); // 删除最后一个字符// 字符串比较">"、"<"// 字符串拼接"+"//注意:使用+对字符串拼接时,要求左右两边至少有一个string对象,即str = "a" + "b";会报错。for (char ch : str) {...} // 遍历(不可修改字符)for (char &ch : str) {...} // 遍历(可修改字符)
字符串流:
#include <sstream>string s;stringstream ssin(s);while(ssin >> s) {...} // 按空格拆分s,例如英语句子拆分单词// 可用如下代码代替while(cin >> word) {...}
char数组难点:
char a[] = {'C', '+', '+'};char b[4] = {'D', '+', '+', '\0'};char c[5] = {'E', '+', '+', '\0'}; // 最后一个位置会补\0cout << a << endl; // 输出"C++D++",因为字符数组a不会自动添加'\0',cout会读取到b的部分
函数中的数组参数:
int size(int a[]) {return sizeof a;}int main() {int a[10];cout << sizeof a << endl; // 4B * 10 = 40Bcout << size(a) << endl;// 8B,虽然函数f能修改实参a的内容,但其本质是一个不同的数组指针指向数组的内存空间.//故对函数内的数组参数a调用sizeof,返回的是数组指针的长度。//在64位系统中,指针的长度等于64b=8B}
默认参数值:
void f(int a, int b = 10) {...} // 需要默认值的变量只能放在靠后的位置
内联:
inline void f() {...} // 编译时把函数体复制到调用函数的位置,减少函数跳转次数
fgets的坑:
fgets会读入\n,因此遍历字符串时,应当用 for (int i = 0; str[i] != '\n'; i++),而不能用 for (int i = 0; str[i]; i++)
结构体:
// 定义struct Node {int val;Node* next;// 构造器Node(int _val): val(_val), next(NULL) {} // 编译更快Node(int _val) {val = _val;}};// 使用Node* p = new Node(1);//注意:链表中的头结点指的是链表第一个结点的地址,而不是结点本身。
类:
class Node {private:...public:...}; // 注意类末尾要加分号!
类与结构体的区别:结构体默认是public,类默认是private。
数组类容器:
#include <vector>// 定义vector<int> a; // 一维数组vector<int> b[N]; // 二维数组// 初始化vector<int> a({1, 2, 3});// 操作a[k]; // 取值a.size(); // 长度a.empty(); // 判空a.clear(); // 清空a.front(); // 读取第1个元素a.back(); // 读取最后1个元素a.push_back(x); // 在末尾插入元素int x = a.pop_back(); // 删除末尾元素并返回int* p = lower_bound(a, a + a.size(), x);// 查找数组在指定范围内大于等于x的元素地址(要求数组有序)int* p = upper_bound(a, a + a.size(), x);// 查找数组在指定范围内大于x的元素地址(要求数组有序)int index = lower_bound(a, a + a.size(), x); - a;// 查找数组在指定范围内大于等于x的元素下标(要求数组有序)int index = upper_bound(a, a + a.size(), x); - a;// 查找数组在指定范围内大于x的元素下标(要求数组有序)// 遍历for (int i = 0; i < a.size(); i++) {...}// 方式1,通过a[i]读取元素值for (vector<int>::iterator i = a.begin(); i < a.end(); i++) {...}// 方式2(迭代器),通过*i读取元素值for (auto i = a.begin(); i < a.end(); i++) {...}// 方式3(迭代器简化版)for (int x : a) {...}// 方式4,通过x读取元素值说明:vector是变长数组,类似java的ArrayLista.begin()返回的是vector第1个元素的地址,而a.end()返回的是最后一个元素的下一个位置的地址a.end() - a.begin() == a.size()*a.begin() == a[0]
#include <queue>/**************************************************** 普通队列queue***************************************************/// 定义queue<int> q;// 操作q.push(x); // 入队(末尾插入元素)int x = q.pop(); // 出队(删除第1个元素)a.front(); // 查看队头元素a.back(); // 查看队尾元素// a.clear()/**************************************************** 优先队列(堆)***************************************************/// 元素为基本类型priority_queue<int> a; // 大根堆priority_queue<int, vector<int>, less<int>> a; // 大根堆priority_queue<int, vector<int>, greater<int>> b; // 小根堆// 元素为自定义类型struct Rec {int a, b;// 大根堆需要自定义类重载<号bool operator< (const Rec& t) const {return a < t.a;}// 小根堆需要自定义类重载>号bool operator> (const Rec& t) const {return a > t.a;}}priority_queue<Rec> a; // 大根堆priority_queue<Rec, vector<Rec>, less<Rec>> a; // 大根堆priority_queue<Rec, vector<Rec>, greater<Rec>> b; // 小根堆// 操作a.push(x); // 插入元素(位置不确定)a.top(); // 查看堆顶元素(大根堆是最大值,小根堆是最小值)a.pop(); // 删除堆顶元素(大根堆是最大值,小根堆是最小值)注意:队列没有clear()方法优先队列插入时无序,输出时有序优先队列存储自定义类型时,需要重载运算符大根堆重载<小根堆重载>
#include <stack>// 定义stack<int> s;// 操作s.push(x); // 入栈s.top(); // 查看栈顶s.pop(); // 出栈(不放回出栈元素!)//注意:stack的pop()方法不像java返回栈顶元素!
#include <deque>// 定义deque<int> q;// 操作q[i] // 随机访问q.begin(); // 队头元素地址,用*q.begin()读取元素q.end(); // 队尾元素地址,用*q.end()读取元素q.front(); // 队头元素值q.back(); // 队尾元素值push_back(); // 队尾插入元素push_front(); // 队头插入元素pop_back(); // 队尾删除元素pop_front(); // 队头插入元素
#include <set>// 定义set<int> s; // 集合multiset<int> ms; // 多重集合(允许元素重复)// 操作s.size();s.empty();s.claer();s.begin();s.end();s.insert(x);s.find(x); // 返回迭代器,可用if(s.find(x) == s.end())判断是否存在元素xs.lower_bound(x); // 返回大于等于x的最小元素的迭代器s.upper_bound(x); // 返回大于x的最小元素的迭代器s.erase(x); // 删除x并返回迭代器s.count(x); // 统计x出现的次数(普通集合只会返回0或1,多重集合可能返回大于1的数)说明:自定义类要求重载<find()、erase()、lower_bound()和upper_bound()都是O(logn)复杂度count()是O(k+logn)复杂度
#include <unordered_set>unordered_set<int> s; // 哈希表unordered_multiset<int> s;
#include <bitset>// 定义二进制串bitset s;// 操作s.count(); // 1的个数s.set(p); // 第p位设为1s.reset(p); // 第p位设为0说明:bitset元素支持位运算符&、|和~等等求x的第k位二进制数:x >> k & 1求x从右起的第1个1:lowbit(x) = x & -x,实际上是原码和补码做与操作。例如110110返回10,11000返回1000
有序对容器
pair:pair<int, int> a = {5, 6};pair<int, int> b = make_pair(5, 6);map:#include <map>map<string, int> m; // 定义// 操作a["a"] = 4; // 类似数组的操作m.insert();m.find();unordered_map:哈希映射,效率更高
algorithm算法库
#include <algorithm>vector<int> a;// 翻转reverse(a.begin(), a.end());reverse(a, a + a.size());// 去重unique(a, a + a.size()); // 返回去重后最后一个元素的地址int m = unique(a, a + a.size()) - a; // 去重后数组的长度a.erase(unique(a.begin(), a.end()), a.end()); // 真删除重复元素// 打乱random_shuffle(a.begin(), a.end());// 排序sort(a.begin(), a.end()); // 升序sort(a.begin(), a.end(), greater<int>()); // 降序bool cmp(int a, int b) {return a - b;} // 自定义比较方法sort(a.begin(), a.end(), cmp); // 自定义排序注意:random_shuffle()常结合ctime的srand( time(0) )使用unique并没有真的删除重复元素,它仅将重复的元素放到非重复元素部分的后边
