别再背STL语法了!用这5个高频场景代码片段,搞定C++刷题和项目
每次打开LeetCode准备刷题,或是开始一个新项目时,你是不是总要先翻出那本厚厚的STL手册?然后花半小时回忆vector怎么删除元素、map如何统计频率?其实真正高效的开发者,手里都有一套经过实战检验的代码模板。下面这5个高频场景的解决方案,能覆盖80%的算法题和项目需求。
1. 高效删除vector中的特定元素
新手常犯的错误是直接遍历删除:
// 错误示范! for(int i=0; i<vec.size(); i++){ if(vec[i] == target) { vec.erase(vec.begin()+i); } }这会导致迭代器失效和漏删问题。正确的O(n)时间复杂度做法是:
vector<int> vec = {1,3,5,7,3,9}; int target = 3; // 正确写法 vec.erase( remove(vec.begin(), vec.end(), target), vec.end() ); // 或者使用迭代器版本(适用于复杂条件) for(auto it=vec.begin(); it!=vec.end();){ if(*it % 2 == 0){ // 删除所有偶数 it = vec.erase(it); } else { ++it; } }提示:
remove算法实际上是把要保留的元素前移,返回新的逻辑终点,需要配合erase完成物理删除
2. 统计元素频率的三种姿势
根据数据特点选择最优方案:
| 场景 | 容器选择 | 时间复杂度 | 代码复杂度 |
|---|---|---|---|
| 数据范围已知且较小 | 数组 | O(n) | ★☆☆ |
| 需要排序遍历 | map | O(nlogn) | ★★☆ |
| 只需快速查询 | unordered_map | O(n) | ★★☆ |
// 方案1:数组计数(适合元素值较小的情况) vector<int> nums = {1,3,2,1,5,2}; int count[100] = {0}; // 假设元素不超过100 for(int num : nums) count[num]++; // 方案2:map自动排序(需要有序输出时) map<string, int> wordCount; for(auto& word : words){ wordCount[word]++; // 等价于: // auto it = wordCount.find(word); // if(it != wordCount.end()) it->second++; // else wordCount.insert({word,1}); } // 方案3:哈希表最快查询 unordered_map<char, int> freq; for(char c : s) freq[c]++;3. Top K问题的优先队列解法
求前K大/小元素时,维护一个大小为K的堆是关键:
// 前K大元素(维护小顶堆) vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int,int> freq; for(int num : nums) freq[num]++; // 定义小顶堆比较函数 auto cmp = [](pair<int,int>& a, pair<int,int>& b){ return a.second > b.second; }; priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp); for(auto& [num, count] : freq){ pq.push({num, count}); if(pq.size() > k) pq.pop(); } vector<int> res; while(!pq.empty()){ res.push_back(pq.top().first); pq.pop(); } return res; }注意:求前K大用小顶堆(淘汰小的),求前K小用大顶堆(淘汰大的)
4. 字符串处理的四件套
字符串问题90%逃不过这四种操作:
- 分割:
getline+stringstream组合
string s = "apple,banana,orange"; stringstream ss(s); string token; while(getline(ss, token, ',')){ cout << token << endl; }- 匹配:KMP算法模板(适合重复匹配)
vector<int> buildNext(string& pattern){ vector<int> next(pattern.size()); for(int i=1, j=0; i<pattern.size(); i++){ while(j>0 && pattern[i]!=pattern[j]) j = next[j-1]; if(pattern[i] == pattern[j]) j++; next[i] = j; } return next; }- 转换:
transform+lambda表达式
string s = "Hello123"; // 转大写 transform(s.begin(), s.end(), s.begin(), [](char c){ return toupper(c); }); // 过滤非字母 s.erase(remove_if(s.begin(), s.end(), [](char c){ return !isalpha(c); }), s.end());- 解析:正则表达式
#include <regex> string log = "Error: 404 Not Found"; smatch matches; if(regex_search(log, matches, regex("Error: (\\d+)"))){ cout << "错误代码:" << matches[1] << endl; }5. 自定义排序的六种写法
STL排序的灵活用法能大幅减少代码量:
vector<vector<int>> intervals; vector<pair<int, string>> products; // 1. Lambda表达式(最常用) sort(intervals.begin(), intervals.end(), [](auto& a, auto& b){ return a[1] < b[1]; }); // 2. 预定义比较函数 bool cmp(pair<int,string>& a, pair<int,string>& b){ if(a.first != b.first) return a.first > b.first; return a.second < b.second; } sort(products.begin(), products.end(), cmp); // 3. 重载运算符(适合自定义类型) struct Task{ int priority; string name; bool operator<(const Task& other) const { return priority > other.priority; // 降序 } }; // 4. 使用函数对象 struct CompareByLength{ bool operator()(const string& a, const string& b){ return a.size() < b.size(); } }; vector<string> words; sort(words.begin(), words.end(), CompareByLength()); // 5. 多条件排序(tuple自动比较) vector<tuple<int,int,string>> records; sort(records.begin(), records.end()); // 自动按元组顺序比较 // 6. 逆序排序(简便写法) sort(nums.rbegin(), nums.rend());实战技巧:容器选择的黄金法则
遇到问题时按照这个决策树选择容器:
是否需要快速查找?
- 是 → 选择
unordered_set/map(O(1)) - 否 → 进入2
- 是 → 选择
是否需要自动排序?
- 是 → 选择
set/map(O(logn)) - 否 → 进入3
- 是 → 选择
是否需要随机访问?
- 是 →
vector或deque - 否 → 进入4
- 是 →
是否先进先出?
- 是 →
queue - 否 →
stack
- 是 →
特殊场景:
- 需要维护极值:
priority_queue - 需要双向操作:
deque - 需要去重+排序:
set - 字符串处理:
string+regex
把这些代码片段保存到你的代码库中,下次遇到类似问题时直接调用。记住,优秀的开发者不是记住所有API,而是知道如何快速找到最适合的解决方案。