C++

 

变量,输入输出,表达式和顺序语句

万能头文件#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 bs存储01串

位运算、常用库函数

与(&),或(|),取反(~),异或(^),左移(«),右移(») 常用操作: (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))