跳到主要内容写给 C 语言用户的 C++ 竞赛入门笔记 | 极客日志C++算法
写给 C 语言用户的 C++ 竞赛入门笔记
面向算法竞赛,快速掌握从C到C++的核心语法与STL用法。涵盖万能头文件、cin/cout、string处理、sort、unique、lower/upper_bound,以及queue、stack、map、set等容器。同时给出关闭同步IO、lambda表达式、优先队列自定义排序、stringstream等进阶技巧。
山野诗人2 浏览 从 C 转到 C++ 做算法题,最直接的好处就是不用再手写 qsort 的比较函数,std::sort 直接用,省事。C++11 之前语法基本兼容 C,加上 STL,切题效率能高出一截。
一个能跑的最简骨架
#include <bits/stdc++.h>
using namespace std;
int main(void) {
return 0;
}
如果只是在 OJ 上刷题,bits/stdc++.h 万能头够用,不用挨个记 <string>、<algorithm>;本地 GCC 环境可能需要配置一下才能用这个头。using namespace std 是为了少敲几个 std::,但正式项目慎用。
输入输出比 C 省心
#include <iostream>
#include <string>
using namespace std;
int main(void) {
int n;
string s;
cin >> n;
cin >> s;
cout << n << "\n" << s << endl;
}
和 printf 那一套格式化占位符说再见,cin/cout 直接认类型,不容易出错。
string 类常用手法
C 里搞字符串总要惦记 strlen、malloc,一不留神就栽。C++ 的 std::string 把内存管理都封装好了,下面几个场景几乎必用。
掐掉前导空格
#include <iostream>
#include <string>
using namespace std;
{
string s;
((cin, s)) {
pos = s.();
s.(, pos);
cout << s << ;
}
cout << ;
}
int main(void)
while
getline
int
find_first_not_of
" "
erase
0
"\n"
"hello , world"
注意 cin 读字符串时会自动跳过前导空白,如果需要保留整行(空格也留下),就用 getline。要去前导零,把 " " 换成 "0" 就行。
子串截取与拼接
#include <iostream>
#include <string>
using namespace std;
int main(void) {
string s = "hello , world";
string s1 = s.substr(0, 5);
string s2 = s.substr(6);
string s3 = s1 + s2;
cout << s1 << s2 << "\n";
cout << s3;
}
substr 很好记:起始位置 + 长度,省略长度就取到尾部。
几个用起来就放不下的操作
sort 远比冒泡快
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
bool comp(int& a, int& b) {
return a > b;
}
int main(void) {
int a[1000] = {0, 1, 1, 4, 5, 1, 4};
sort(a + 1, a + 1 + 6);
for (int i = 1; i <= 6; i++) cout << a[i];
cout << "\n";
sort(a + 1, a + 1 + 6, comp);
for (int i = 1; i <= 6; i++) cout << a[i];
}
复杂度 O(nlogn)。comp 里用了引用 & 避免拷贝,数据量不大时不加也没事,但加上是习惯。
数组去重一条龙
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(void) {
int a[1000] = {0, 1, 1, 4, 5, 1, 4};
sort(a + 1, a + 1 + 6);
int* pos = unique(a + 1, a + 1 + 6);
int new_count = pos - (a + 1);
for (int i = 1; i <= new_count; i++) cout << a[i] << " ";
}
unique 不真正删除元素,所以必须用返回值算新长度,或者搭配 vector 的 erase。
二分查找现成的轮子
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main(void) {
int a[105];
for (int i = 1; i <= 100; i++) a[i] = i;
int* lb1 = lower_bound(a + 1, a + 101, 25);
int index1 = lb1 - a;
cout << "elem is " << *(lb1) << " index is " << index1 << endl;
int* ub2 = upper_bound(a + 1, a + 101, 25);
int index2 = ub2 - a;
cout << "elem is " << *(ub2) << " index is " << index2 << endl;
int* result = lower_bound(a + 1, a + 101, 101);
if (result == a + 101) {
cout << "lower_bound(101) return a+101\n";
}
return 0;
}
lower_bound 和 upper_bound 要求区间有序,返回值是指针(或迭代器),相减得到下标。
STL 容器:栈、队列、映射、集合
先进先出队列
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main(void) {
queue<int> q;
for (int i = 0; i < 10; i++) q.push(i);
while (!q.empty()) {
cout << q.front() << " " << q.back() << "\n";
q.pop();
}
deque<int> qu;
for (int i = 0; i < 10; i++) {
qu.push_back(i);
}
while (!qu.empty()) {
cout << qu.front() << " " << qu.back() << "\n";
qu.pop_back();
qu.pop_front();
}
}
queue 内部通常用数组实现,所有操作 O(1)。拿不准有多少元素时,while (!q.empty()) 是标准套路。
后进先出栈
#include <algorithm>
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
int main(void) {
stack<int> s;
for (int i = 0; i < 10; i++) s.push(i);
while (!s.empty()) {
cout << s.top() << " ";
s.pop();
}
}
接口和队列几乎一样,只是 front 换成了 top,弹出的是最后进去的那个。
键值对 map
#include <algorithm>
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <map>
using namespace std;
int main(void) {
map<int, int> mp;
int temp = 114514;
for (int i = 0; i < 10; i++) {
if (i < 4) mp[i] = temp * (10 - i);
else mp.insert({i, temp * (10 - i)});
}
for (auto& [key, value] : mp) {
cout << "mp[" << key << "] = " << value << "\n";
}
if (mp.find(2) != mp.end()) cout << "Yes";
if (mp.find(10) == mp.end()) cout << "No";
cout << "\n";
int idx = 0;
while (1) {
mp.erase(idx);
idx++;
if (mp.empty()) {
cout << "已弹出所有元素";
break;
}
}
return 0;
}
底层是红黑树,插入、查找、删除都是 O(logn)。如果不需要有序,改用 unordered_map,哈希表 O(1) 更香。
自动排序去重的 set
#include <algorithm>
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
using namespace std;
int main(void) {
set<int> s;
for (int i = 0; i < 100; i++) s.insert(0);
s.insert(5);
s.insert(3);
s.insert(114514);
for (auto i : s) cout << i << " ";
cout << "\n";
if (s.find(3) != s.end()) cout << "Yes" << " ";
if (s.find(6) == s.end()) cout << "No" << " ";
cout << "\n";
cout << s.size() << "\n";
for (auto i : s) s.erase(i);
cout << s.size() << "\n";
return 0;
}
相当于只有 key 的 map,集合内元素默认升序且唯一。复杂度也是 O(logn)。
竞赛常用的几个进阶招式
关掉同步,提速 IO
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
碰到大量输入输出的题,加上这三行,能甩开 C 风格 IO 的差距。
lambda 让比较就地解决
std::vector<int> arr = {1, 1, 4, 5, 1, 4};
std::sort(arr.begin(), arr.end(), [](int& a, int& b) { return a > b; });
不需要再单独写一个 comp 函数,lambda 表达式直接嵌在调用处,可读性不错。
优先队列自定义排序
struct Node {
int x, y;
bool operator<(Node &a) const {
return x < a.x;
};
};
int main(void) {
std::priority_queue<Node> q;
}
priority_queue 默认是大顶堆,如果存的是结构体,必须重载 <,否则不知道怎么比大小。插入弹出 O(logn),取堆顶 O(1)。
stringstream 做转换和分割
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int main(void) {
string s = "1 2 3 4 5 6";
vector<int> arr;
stringstream stream(s);
int num;
while (stream >> num) {
arr.push_back(num);
}
for (auto i : arr) cout << i << " ";
cout << "\n";
string data = "apple,banana,orange,grape";
stringstream ss(data);
string fruit;
while (getline(ss, fruit, ',')) cout << fruit << endl;
return 0;
}
stringstream 既能将带空格的字符串拆成数字,也能按指定分隔符切分,在一道题里做完输入预处理。
这些差不多就是 C++ 基础部分中最实用的内容了。搞开发的话,异常处理、内存管理、模板之类还得深挖,但单纯为了打比赛或者写点小工具,这些已经覆盖了八成的场景。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online