变量,输入输出,表达式和顺序语句
万能头文件#include <bits/stdc++.h>
使用c的输入输出,最好加上#include <cstdio>
INT_MAX(2^31 − 1), INT_MIN(−2^31)
c++里的空节点三种写法:0,NULL,nullptr
判断语句
循环语句
数组
字符串
字符与整数的联系——ASCII码
// 每个常用字符都对应一个-128~127的数字,二者之间可以相互转化
char c = 'a';
cout << (int)c << endl;
int a = 66;
cout << (char)a <<endl;
// 常用ASCII值:'A'~'Z'是65~90,'a'~'z'是97~122,'0'~'9'是48~57,字符可以参与运算,运算时会将其当做整数
int a = 'B' - 'A';
int b = 'A' * 'B';
char c = 'A' + 2;
字符数组
字符串就是字符数组加上结束符’\0’ 可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个’\0’字符,因此字符数组的长度至少要比字符串的长度多1
char a1[] = {'c', '+', '+'}; // 列表初始化,没有空字符
char a2[] = {'c', '+', '+', '\0'}; // 列表初始化,含有显示的空字符
char a3[] = "c++"; // 自动添加字符串结尾的空字符
char a4[6] = "Daniel"; // 错误,没有空间可存放空字符
// 字符数组的输入输出
char str[100];
cin >> str;
gets(str); // 读入一行字符串(包括空格)
cout << str <<endl;
printf("%s", str);
// 字符数组的常用操作
#include <string.h> // 下面几个操作需要引入头文件
strlen(str) // 求字符串长度
strcmp(a, b); // 比较两个字符串的大小,a < b 返回 -1,a == b 返回0,a > b 返回1,这里的比较方式是字典序
strcpy(a, b); // 将b复制给a
标准库类型string
可变长的字符序列,比字符数组更加好用。
字符串里可以包含空格,如:”hello world”
str.substr(字符串起始位置start, length),如果是到最后,length可不写
字符数组:str.c_str()
将字符串转换为整数:str.atoi(str.c_str()), str.stoi(str)
#include <string> // 头文件
string s1; // 默认初始化,s1是一个空字符串
string s2 = s1; // s2是s1的副本
string s3 = "hiya"; // s3是该字符串字面值的副本
string s4(10, 'c'); // s4的内容是 cccccccccc
string s;
getline(cin, s); // 使用getline读取一整行,直接cin >> s,输入“hello world”,s为“hello”,而用getline,s为“hello world”
printf("%s", s.c_str()); // 注意,不能用printf直接输出string,需要写成printf("%s", s.c_str());
s.size(); // size()函数返回字符串长度,如“as ad”,s.size()为5(注意size是无符号整数,因此s.size() <= -1 一定成立)
s.empty(); // empty()函数返回一个bool类型
// 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
string s1 = "hello", s2 = "world";
string s3 = s1 + "," + s2 + "\n";
string s4 = s1 + ","; // 正确
string s5 = "hello" + ","; // 错误:两个运算对象都不是string
string s6 = s1 + "," + "world"; // 正确
string s7 = "hello" + "," + s2; // 错误:不能把字面值直接相加,运算是从左到右的
// 处理string对象中的字符,可以将string对象当成字符数组来处理
for (int i = 0; i < s.size(); i ++ ) cout << s[i] << endl;
for (char c : s) cout << c <<endl;
for (char &c : s) c = 'a';
cout << s <<endl;
函数
类、结构体、指针和引用
类:
class Solution {
public:
string leftRotateString(string str, int n) {
return str.substr(n) + str.substr(0, n);
}
};
结构体:结构体和类的作用是一样的。不同点在于类默认是private,结构体默认是public。
struct 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, person[100];
指针和引用:指针指向存放变量值的地址
#include <iostream>
using namespace std;
int main() {
int a = 10;
int *p = &a;
*p += 5;
cout << *p <<endl;
return 0;
}
增加头结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
auto dummy = new ListNode(-1);
dummy->next = head;
STL
所有的STL容器都支持size()和empty()函数。queue(循环队列、优先队列)、stack(栈)没有clear()函数,其他STL容器都有。
#include <vector>
vector是变长数组,支持随机访问,不支持在任意位置O(1)插入。为了保证效率,元素的增删一般应该在末尾进行
#include <vector> // 头文件
vector<int> a; // 相当于一个长度动态变化的int数组
vector<int> b[233]; // 相当于第一维长233,第二维长度动态变化的数组
struct rec{...};
vector<rec> c; // 自定义的结构体类型也可以保存在vector中
// vector常用函数
a.size(); // size()函数返回vector的实际长度(包含元素的个数)
a.empty(); // empty()函数返回一个bool类型,表明vector是否为空。二者的时间复杂度都是O(1)
a.clear(); // clear()函数把vector清空
a.push_back(4); // push_back(x)函数:往a的最后面加一个元素x
a.pop_back(); // pop_back()函数:删除a的最后面一个元素
a.front(); // front()函数返回vector的第一个元素,等价于*a.begin()和a[0]
a.back(); // back()函数返回vector的最后一个元素,等价于*--a.end()和a[a.size() - 1]
// 迭代器
vector<int>::iterator it; // 一个保存int的vector的迭代器声明方法。迭代器就像STL容器的“指针”,可以用星号“*”操作符解除引用。vector的迭代器是“随机访问迭代器”,可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。
vector<int>::iterator it = a.begin(); // begin()函数返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()与a[0]的作用相同
vector<int>::iterator it = a.end(); // 所有容器都可以视作一个“前闭后开”的结构,end()函数返回vector的尾部,即第n个元素再往后的边界。*a.end()与a[n]都是越界访问,其中n = a.size()
// 遍历vector<int> a
vector<int> a({1, 2, 3}); // 或者vector<int> a = {1, 2, 3};
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; // vector<int>::iterator可以换成auto(auto让编译器自己推断类型)
for (int x : a) cout << x << endl;
#include <queue>
头文件queue主要包括循环队列queue和优先队列priority_queue两个容器。
#include <queue> // 头文件
// 循环队列
queue<int> q;
strcut rec{...}; queue<rec> q;
// 循环队列queue常用函数
q.size(); q.empty();
q.push(1); // 在队头插入一个元素
q.pop(); // 弹出队尾元素
q.front(); // 返回队头元素
q.back(); // 返回队尾元素
// 优先队列
priority_queue<int> q; // 大根堆
priority_queue<int, vector<int>, greater<int>> q; // 小根堆
priority_queue<pair<int, int>> q;
struct rec{
int a, b;
bool operator< (const rec& t) const {
return a < t.a;
}
}; // 大根堆,结构体rec中必须定义小于号
priority_queue<rec> q;
priority_queue<rec1, vector<rec1>, greater<rec1>> q; // 如果是小根堆,rec需要重载大于号
// 优先队列priority_queue
priority_queue<int> a; // 大根堆
a.size(); a.empty();
a.push(1); // 把元素插入堆
a.pop(); // 删除堆顶元素(最大值)
a.top(); // 取堆顶元素(最大值)
// 循环队列、优先队列、栈没有clear()函数,其他STL容器都有
q = queue<int>(); // 重新初始化即可清空
#include <stack>
stack<int> s;
s.size(); s.empty();
s.push(1); // 向栈顶插入
s.pop(); // 弹出栈顶元素
s.top(); // 取栈顶元素
#include <deque>
双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间(vector在尾部插入删除O(1),在头部是O(n));与queue相比,deque像数组一样支持随机访问。
#include <deque> // 头文件
deque<int> q;
q.size(); q.empty(); q.clear();
q.front(); q.back(); // 队头/队尾元素
q.push_back(); q.pop_back(); // 从队尾 入队/出队
q.push_fornt(); q.pop_front(); // 从队头 入队/出队
q.begin(); q.end(); // 头/尾迭代器
#include <set>
头文件set主要包括set和multiset两个容器,分别是”有序集合”和”有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一颗红黑树,它们支持的函数基本相同。
// set
set<int> s; // 元素不能重复
struct rec{
int x, y;
bool operator< (const rec& t) const{
return x < t.x;
}
};
set<rec> s; // 结构体rec中必须定义小于号
// multiset
multiset<double> s; // 元素可以重复
// 迭代器
// set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持“++”和“--”两个与算术相关的操作
set<int>::iteartor it; // 若把it++,则it会指向“下一个”元素。这里的下一个元素是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it--,则it将会指向“上一个”元素。
s.begin(); // 指向集合中最小元素的迭代器
s.end(); // 指向集合中最大元素的下一个位置的迭代器。和vector一样,是一个“前闭后开”的形式,因此--s.end()是指向集合中最大元素的迭代器。
// 常用函数,set和multiset的常用函数都差不多
s.size(); s.empty(); s.clear();
s.insert(x); // 插入x,在set容器中若元素已存在,则不会重复插入该元素,对集合的状态无影响。时间复杂度为O(logn)
s.erase(it); // 从s中删除迭代器it指向的元素,时间复杂度为O(logn)
s.erase(x); // 从s中删除所有等于x的元素,时间复杂度为O(k + logn),k是被删除的元素的个数
s.find(x); // 查找x,返回指向该元素的迭代器,若不存在则返回s.end()。时间复杂度为O(logn)
s.lower_bound(x); // 查找大于等于x的元素中最小的一个,并返回指向该元素的迭代器
s.upper_bound(x); // 查找大于x的元素中最小的一个,并返回指向该元素的迭代器
s.count(x); // 返回集合s中等于x的元素个数,时间复杂度为O(k + logn),其中k为元素x的个数
#include <map>
map容器是一个键值对key-value的映射,其内部实现是一颗以key为关键码的红黑树。Map的key和value可以是任意类型,其中key必须定义小于号运算符。与set类似,map头文件也有multimap容器。
map<key_type, value_type> name; // map写法
map<long long, bool> vis;
map<string, int> hash;
map<pair<int, int>, vector<int>> test;
// 迭代器
map<int, int>::iterator it;
m.begin(); m.end();
// 常用函数
m.size(); m.empty(); m.clear();
map<string, int> m;
m.insert({"abc", 1});
m.erase(it);
m.earse("abc");
m.find("abc"); // 查找key为"abc"的二元组,返回指向{"abc", 1}的键值对,*m.find("abc") 解引用后得到的是 pair<const string, int> 对象(包含 first(键)和 second(值)两个成员)
cout << (*m.find("abc")).first << endl; // 输出键“abc”
cout << (*m.find("abc")).first << endl; // 输出值1
#include <unordered_set>和#include <unordered_map>
#include <unordered_set>无序集合,底层实现是哈希表,与#include <set>类似,头文件unordered_set也包括unordered_set容器(不能存储重复元素)和unordered_multiset(可存储重复元素)容器。
#include <unordered_set>无序map,也包括unordered_map容器和unordered_multimap容器。
unordered_set<int> a; // 不能存储重复元素
unordered_multiset<int> b; // 能存储重复元素
unordered_map<int, int> c;
unordered_multimap<int, int> d;
其他容器头文件:#include <bitset>,bitset
位运算、常用库函数
与(&),或(|),取反(~),异或(^),左移(«),右移(») 常用操作: (1)求x的第k位数字,x » k & 1
#include <iostream>
using namespace std;
int main() {
int x = 13;
for (int i = 5; i >= 0; i -- ) cout << (x >> i & 1); // 输出001101(13的二进制表示)
return 0;
}
(2)lowbit(x) = x & -x,返回x的最后一位1,(x & -x) 即 (x & (~x + 1))