一些c++的基础知识

  • 常用头文件

    • iostream : cin, cout, scanf, printf
    • cstdin : scanf, printf
    • cmath
    • bits/stdc++.h : 万能头文件,但引用时间稍长
  • swap(): 新编译器中,swap包含在iostream中,调用swap(int x,int y)即可;在旧编译器中,需要使用algorithm库

  • 逗号运算符:C++的,运算符对逗号前后的表达式进行运算,然后舍弃前一个表达式的返回值,仅仅返回最后一个表达式的返回值

    if(cin >> a >> b, a > 0, b > 0); // if只看最后一个表达式b>0,不关心cin和a>0
    
  • 处理T组数据

    int T = 0;
    cin >> T;
    while(T--){
        // 处理过程
    }
    return 0;
    
  • char数组问题

    char a[] = {'C', '+', '+'};
    char b[4] = {'D', '+', '+', '\0'};
    char c[5] = {'E', '+', '+', '\0'};          // 最后一个位置会补\0
    
    cout << a << endl;          // 输出"C++D++",因为字符数组a不会自动添加'\0',cout会读取到b的部分
    
  • cstring:?

    memset(array, value, end) 
    
  • 0x3f3f3f比较大的值

    int h[100];
    memset(h, 0x3f, sizeof h); // memset按照字节初始化, h为int类型,int有四个字节,每个字节初始化为0x3f即可。
    
输入输出

输入

  • scanf("%d%d", &a, &b);

  • cin >> a >> b;

  • 字符的读入

    scanf("%c%c", &a, &b);
    cin >> a >> b;
    
  • cin, cout输入输出加速代码

    cin.tie(0);
    ios::sync_with_stdio(0);
    

    Tips: 加速代码仅针对cin, cout,添加加速代码后,cin, cout不能与scanf, printf混用。

输出

  • printf("%d%d", a, b);
  • cout << a << b;
  • puts()

基本模板

#include <iostream>
using namespace std;
int main()
{
	int a = 0, b = 0;
	cin >> a >> b;
	cout << a + b << endl;
    return 0;
}
STL

vector

变长数组

vector<int> q(length, value); // 指定数组长度为length,指定默认值为value。
push_back() // 插入
size() // 查看元素个数,O(1)
clear() // 清空
empty() // 是否为空
front() / back() // 返回数组第一个 / 最后一个 元素
push_back() // 从后面添加一个数
pop_back() // 弹出最后一个元素
begin() / end() // 迭代器:第0个元素的位置 和 最后一个元素后一个位置
[] // 支持随机选址
// 支持字典序比较 
#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读取元素值
    cout << *i << " ";
}     
for (auto i = a.begin(); i < a.end(); i++) {cout << *i << " ";}    // 方式3(迭代器简化版)
for (auto x : a) {...}                                             // 方式4,通过x读取元素值

// 字典序比较
vector<int> a(4, 3), b(3, 4);
if(a < b) puts("a < b");
// output : "a < b"

a.begin()返回的是vector第1个元素的地址,而a.end()返回的是最后一个元素的下一个位置的地址,取值需要加上*

stack

stack<int> stk;//定义
stk.push(1);//从栈顶插入
stk.top();//返回栈顶元素
stk.pop();//从栈顶弹出
stk.empty();//是否为空

queue

队列

queue<int> q;//定义一个队列q
q.push(1);//像队尾插入1
q.pop();//弹出队头
q.empty();//返回队列是否为空
q.front();//返回队头元素 
q.back();//返回队尾元素
q = queue<int>();//已定义q时,重新定义一个队列。

deque

双端队列,加强版vector,支持双端添加、弹出元素。效率较低

deque<int> dq;//定义一个双端队列
deque.size();//返回长度
//插入
dq.push_front(1);//向队头插入
dq.push_back(1);//向队尾插入
//删除
dq.pop_front();//删除队头
dq.pop_back();//删除队尾
//访问
dq.front();//访问第一个元素
dq.back();//访问最后一个元素
cout << dq[0] << endl;//下标访问

dq.begin();//开头指针迭代器
dq.end();//末尾指针迭代器
dq.clear();//清空
dq.empty();//是否为空

priority_queue

优先队列,可理解为堆。

push() //插入一个元素
top() //返回堆顶元素
pop() //弹出堆顶元素
empty() //判断堆是否为空
size() //返回元素个数

Tips: 默认定义为大根堆,可以采用插入-x的方式实现小根堆的效果,也可以直接定义小根堆。

// 头文件
#include<queue>
#include<vector>
// 定义
priority_queue<int> heapone;// 定义大根堆
priority_queue<int, vector<int>, greater<int>> heaptwo;//定义小根堆
// 操作
heaptwo.top();//返回堆顶元素
heaptwo.push(1);//向堆插入一个数。
heaptwo.pop();//弹出堆顶元素
heaptwo.empty();//判断堆是否为空
heaptwo.size();//返回元素个数

string

字符串

string line;//定义字符串
getline(cin, line);//读入字符串方式1
cin >> line;//读入字符串方式2
//推荐cin读入字符串
line[1];//访问元素
line.substr(x, length);//取出从x开始的长度为length的子串 length = min(length, line.size())
line.substr(1); //返回从1开始的整个子串
line.size();//返回元素个数
line.length();//和size相同
line.empty();//判断是否为空
ling.clear();//清空
line.push_back();//插入字符
  • 输入

    #include <string>
    string str;
    
    cin >> str;         // 不能读取含空格、换行符的字符串
    getline(cin, str);  // 能读取含空格的字符串,同时自动去掉换行符\n
    
  • 操作

    string str;
    
    string s(5, 'a');           // 构造重复字符的字符串
    str.empty();                // 判空
    str.clear();			   // 清空
    str.size();                 // 长度,与stelen()不同的是,这个复杂度是O(1),不用额外的变量保存
    str.c_str();                // 转成char数组,此时才可用printf输出
    str.substr(begin, length);  // 子串
    str.pop_back();             // 删除最后一个字符
    // 字符串比较">"、"<"
    // 字符串拼接"+"
    
    for (char ch : str) {...}   // 遍历(不可修改字符)
    for (char &ch : str) {...}  // 遍历(可修改字符)
    
  • 字符串流*

    #include <sstream>
    
    string s;
    stringstream ssin(s);
    while(ssin >> s) {...}      // 按空格拆分s,例如英语句子拆分单词
    
    // 可用如下代码代替
    while(cin >> word) {
    ...
    }
    

pair

组合数据结构,排序时默认按元素结构顺序排序,可以嵌套。

  • first: 第一个元素
  • second: 第二个元素
  • 支持字典序比较:以first为第一关键字,以second为第二关键字(字典序)
pair<int, string> tone;//定义二元组
pair<int, pair<int, string>> ttwo;//定义三元组
pair<int, int> tthree[10086];//定义pair数组
cout << tone.first << endl;//查看tone的第1项。
cout << tone.second << endl;//查看tone的第二项
cout << ttwo.second.second << endl;//循环嵌套

// 构造一个pair
p = make_pair(10, 'ss');
p = {10, 'ss'}; //c++11 特性

map

字典,内容为'key':value形式

#include <map>

map<int, string> Map;//定义一个以int为索引,拥有指向string的指针。
pair<int, string> in = {000, "cht"};
//插入元素
Map.insert(in); //插入一个pair
Map[1] = 'hello'; //像数组一样访问,时间复杂度O(logn)

Map.find(000);//返回元素位置
Map.erase(000);//删除元素(至此cht被踢出了Map),参数是pair或者迭代器
Map.size();//返回大小
Map.begin();//头部迭代器
Map.end();//尾部迭代器
Map.clear();//清空
Map.count();//返回元素个数
Map.empty();//返回map是否为空
Map[0];//下标访问

lower_bound(); //返回 大于等于x 的 最小的数,不存在则返回end()迭代器
upper_bound(); //返回 大于x 的 最小的数,不存在则返回end()迭代器

set

multiset / set

  • set: 集合,存储无重复元素,操作时间复杂度nlogn

  • multiset: 集合,可存储重复元素。

set<int> S;
S.begin();//返回第一个元素的地址
S.end();//返回最后一个元素的地址
S.clear();//清空
S.empty();//是否为空
S.size();//返回长度
S.count(1);//返回1的个数
S.find(1);//寻找,不存在则返回end()迭代器
S.erase(1);//删除
/*
    a. 输入是一个数x,则删除所有x。
    b. 输入是一个迭代器,删除这个迭代器。
*/
S.insert(1);//插入
// 核心操作
lower_bound(); //返回 大于等于x 的 最小的数,不存在则返回end()迭代器
upper_bound(); //返回 大于x 的 最小的数,不存在则返回end()迭代器

list

相当于双向链表。

#include<list>

// 具体操作
list<int> L;
L.push_back(1);//插入
L.empty();//判断是否为空
L.begin();//返回头结点
L.end();//返回尾部节点
L.insert(L.begin(), 1);//在指定位置插入
L.erase(L.begin());//删除
L.erase(L.begin(), L.end());//区间删除
L.clear();//清空list
L.push_front();//插入
L.pop_front();//删除
L.pop_back();//删除
L.size();//元素个数

unordered_

unordered_set, unordered_map, unordered_multiset, unordered_multimap 哈希表

基础操作类似于set, map,但增删改查的时间复杂度为0(1)

不支持lower_bound() / upper_bound()

bitset

压位,节省空间。

bitset<10000> s; // 声明一个10000位的bitset
// 支持与或运算 ~, &, |, ^
// 支持移位操作 >>, <<
// 支持比较操作 ==, !=
// 支持随机读取 []

count(); // 返回有多少个1

any(); // 判断是否至少有一个1
none(); // 判断是否全为0

set(); // 把所有位都置为1
set(k, v); // 将第k位变成v
reset(); // 把所有位置变成0
flip(); // 等价于~
flip(k); // 把第k位取反
其他

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);                      // 自定义排序

参考

  1. https://www.acwing.com/blog/content/4017/
  2. https://www.acwing.com/blog/content/3122/
  3. https://www.acwing.com/activity/content/11/