为什么要转 C++
对于 C 语言,入门虽简单,但深入后维护成本较高且功能受限。例如 C 语言的排序需要手写 qsort 比较函数,而 C++ 的 std::sort 则便捷得多。此外,C++ 提供了丰富的 STL 容器(如 queue, stack, vector)来简化数据结构的使用。
值得注意的是,在 C++11 标准之前,C++ 与 C 非常相似。如果目标是算法竞赛,基本可以理解为 C++ = C + STL,迁移起来几乎没有门槛。
C++ 语言基础
初学者只需掌握以下基础框架即可开始编写程序:
#include <bits/stdc++.h>
using namespace std;
int main() {
return 0;
}
这段代码与 C 语言类似,主要区别在于 using namespace std; 和头文件 #include <bits/stdc++.h>。前者用于引入命名空间,后者是竞赛专用的万能头文件(GCC 环境支持),后续若专注于算法竞赛,无需记忆具体头文件。
C++ 如何输入输出
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
string s;
cin >> n;
cin >> s;
cout << n << "\n" << s << endl;
return 0;
}
iostream 对应 C 语言的 stdio.h,而 string 是 C++ 特有的字符串对象。相比 C 语言中 scanf/printf 需要记忆格式符(如 %d, %s),C++ 使用 cin/cout 更加直观。
C++ string 相关
C 语言处理字符串较为繁琐,C++ 的 string 类大大简化了这一过程。以下是常用的字符串操作方法。
去除前导空格
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
while (getline(cin, s)) {
size_t pos = s.find_first_not_of(" ");
if (pos != string::npos) {
s.erase(0, pos);
}
cout << s << "\n";
}
cout << "hello, world";
return 0;
}
注意:cin 读取单词时会自动跳过前导空格,但若需处理整行包含空格的文本,请使用 getline。若要去除前导零,只需将空格字符替换为 "0"。
提取和拼接子串
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "hello, world";
// 从第 0 个位置开始截取长度为 5 的子串
string s1 = s.substr(0, 5);
// 从第 6 个位置开始截取后面的所有字符
string s2 = s.substr(6);
// 拼接字符串
string s3 = s1 + s2;
cout << s1 << s2 << "\n";
cout << s3;
return 0;
}
substr 成员函数用于截取子串,参数分别为起始位置和长度(省略长度则表示截至末尾)。
一些常用的 C++ 操作
C++ sort 排序函数的用法
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
bool comp(int& a, int& b) {
return a > b;
}
int main() {
int a[1000] = {0, 1, 1, 4, 5, 1, 4};
// 默认升序排序,范围 [a+1, a+7)
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];
return 0;
}
std::sort 的时间复杂度为 O(nlogn),远快于冒泡排序。第三个参数可传入自定义比较函数,使用引用传递(&)可避免数值复制,提升性能。
数组去重
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
// 原始数组方法
int a[1000] = {0, 1, 1, 4, 5, 1, 4};
sort(a + 1, a + 1 + 6);
// unique 返回去重后的逻辑末尾迭代器
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] << " ";
cout << "\n";
// vector 动态数组方法
vector<int> arr = {0, 1, 1, 4, 5, 1, 4};
sort(arr.begin() + 1, arr.begin() + 1 + 6);
auto pos_vec = unique(arr.begin() + 1, arr.begin() + 1 + 6);
arr.erase(pos_vec, arr.end());
return 0;
}
unique 会将重复元素移动到序列末尾并返回新逻辑末尾,配合 erase 或计算索引即可得到去重后的有效数据。
二分查找
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int a[105];
for (int i = 1; i <= 100; i++) a[i] = i;
// lower_bound: 查找第一个大于等于 val 的元素
int* lb1 = lower_bound(a + 1, a + 101, 25);
cout << "lower_bound(25): elem=" << *lb1 << " index=" << (lb1 - a) << endl;
// upper_bound: 查找第一个大于 val 的元素
int* ub1 = upper_bound(a + 1, a + 101, 25);
cout << "upper_bound(25): elem=" << *ub1 << " index=" << (ub1 - a) << endl;
// 边界情况处理
int* result = lower_bound(a + 1, a + 101, 101);
if (result == a + 101) {
cout << "lower_bound(101) out of range\n";
}
return 0;
}
lower_bound 和 upper_bound 均基于二分查找,时间复杂度为 O(logn)。注意返回值可能指向数组末尾,使用时需判断是否越界。
STL 容器相关
STL 提供了多种高效的数据结构,涵盖栈、队列、映射等常见需求。
常见的 STL 容器使用
常见队列
#include <iostream>
#include <queue>
#include <deque>
using namespace std;
int main() {
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> dq;
for (int i = 0; i < 10; i++) dq.push_back(i);
while (!dq.empty()) {
cout << dq.front() << " " << dq.back() << "\n";
dq.pop_front();
}
return 0;
}
队列遵循先进先出(FIFO)原则,内部通常基于数组实现,操作复杂度为 O(1)。deque 作为双端队列,支持在头部和尾部进行插入和删除。
栈
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack<int> s;
for (int i = 0; i < 10; i++) s.push(i);
while (!s.empty()) {
cout << s.top() << " ";
s.pop();
}
return 0;
}
栈遵循后进先出(LIFO)原则,常用操作包括 push, pop, top。
map(键值对)
map 是一种关联容器,内部通常由红黑树实现,支持键值映射。插入和查找的时间复杂度为 O(logn)。
#include <iostream>
#include <map>
using namespace std;
int main() {
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)});
}
// 遍历 map
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";
// 清空 map
mp.clear();
return 0;
}
unordered_map 则是基于哈希表实现,查找和插入平均复杂度为 O(1),但无序。根据场景选择即可。
set(集合)
set 类似于只有一个值的 map,自动有序且去重。
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
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 << "Size: " << s.size() << "\n";
return 0;
}
C++ 进阶相关
标准输入输出加快
在大量 IO 操作时,关闭同步可显著提升速度:
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
lambda 表达式
匿名函数可用于简化排序比较器等场景:
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> arr = {1, 1, 4, 5, 1, 4};
sort(arr.begin(), arr.end(), [](int& a, int& b) {
return a > b;
});
return 0;
}
优先队列自定义排序
优先队列(堆)常用于获取最大或最小值。对于自定义结构体,需重载比较运算符:
#include <queue>
struct Node {
int x, y;
bool operator<(const Node &a) const {
return x < a.x;
}
};
int main() {
priority_queue<Node> q;
return 0;
}
stringstream 流
用于字符串与数字之间的转换或分割:
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main() {
// 字符串转整数
string s = "1 2 3 4 5 6";
vector<int> arr;
stringstream stream(s);
int num;
while (stream >> num) arr.push_back(num);
// 字符串分割
string data = "apple,banana,orange";
stringstream ss(data);
string fruit;
while (getline(ss, fruit, ',')) cout << fruit << endl;
return 0;
}
结语
以上内容涵盖了 C++ 的基础语法与常用库特性。无论是从事 C++ 开发还是参与算法竞赛,掌握这些内容都是必要的基石。虽然文中区分了基础与进阶,但在实际应用中,灵活运用 STL 和现代 C++ 特性往往能事半功倍。

