后悔沒早點知道!這 18 個 STL 算法讓代碼效率暴增 200%
哥們,說實話,你是不是還在用那種寫了10行代碼才能搞定的事情?比如找個最大值要寫個for循環,判斷個條件要if套if?
我之前也是這樣,直到我發現了STL里藏著的這些"黑科技"。用了之后,代碼不僅變短了,運行速度還提升了幾倍!同事們都問我是不是偷偷上了什么培訓班。
今天就把這些壓箱底的寶貝分享給你,保證你看完之后會拍大腿說:"臥槽,還有這種操作!"
1. std::iota - 數列生成器,告別手寫循環
以前的你:
vector<int> nums(10);
for(int i = 0; i < 10; i++) {
nums[i] = i + 1;
}
現在的你:
#include <numeric>
vector<int> nums(10);
std::iota(nums.begin(), nums.end(), 1);
// 輸出:1 2 3 4 5 6 7 8 9 10
看到沒?一行代碼搞定!iota這個名字來自希臘字母,專門用來生成連續數列。想生成1到100?想生成-5到5?隨你折騰!
vector<int> countdown(5);
std::iota(countdown.begin(), countdown.end(), -2);
// 輸出:-2 -1 0 1 2
2. std::adjacent_find - 找重復鄰居的神器
你有沒有遇到過這種情況:要找數組里第一個重復的相鄰元素?比如找字符串里的連續重復字符?
笨辦法:
string s = "programming";
int pos = -1;
for(int i = 0; i < s.length() - 1; i++) {
if(s[i] == s[i+1]) {
pos = i;
break;
}
}
聰明做法:
#include <algorithm> // 這個是關鍵!adjacent_find在這里
#include <string>
string s = "programming";
auto it = std::adjacent_find(s.begin(), s.end());
if(it != s.end()) {
cout << "找到重復字符: " << *it << " 位置: " << it - s.begin() << endl;
}
// 輸出:找到重復字符: m 位置: 6
這個函數還能自定義比較條件!比如找第一對差值大于10的相鄰數字:
vector<int> nums = {1, 3, 5, 18, 20};
auto it = std::adjacent_find(nums.begin(), nums.end(),
[](int a, int b) { return abs(a - b) > 10; });
// 找到5和18這對冤家
3. std::inner_product - 向量運算的瑞士軍刀
這個函數名字聽起來很數學,但其實超級實用!不僅能計算點積,還能做各種神奇的運算。
計算兩個數組的點積:
#include <numeric>
vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
int result = std::inner_product(a.begin(), a.end(), b.begin(), 0);
cout << result << endl; // 輸出:32 (1*4 + 2*5 + 3*6)
更騷的操作 - 自定義運算:
// 計算兩個數組對應位置的差值之和
vector<int> prices_yesterday = {100, 200, 150};
vector<int> prices_today = {105, 195, 160};
int total_change = std::inner_product(
prices_today.begin(), prices_today.end(),
prices_yesterday.begin(), 0,
std::plus<>(), // 累加
std::minus<>() // 相減
);
cout << "總體價格變化: " << total_change << endl; // 輸出:10
4. std::nth_element - 找第N大元素的速度怪獸
要找第K大的元素,你是不是還在排序然后取下標?兄弟,這樣太慢了!
傳統做法(慢):
vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
sort(nums.begin(), nums.end());
cout << nums[2] << endl; // 第3小的元素
高效做法:
#include <algorithm>
vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
std::nth_element(nums.begin(), nums.begin() + 2, nums.end());
cout << nums[2] << endl; // 輸出:2
// 而且nums[2]左邊的都比它小,右邊的都比它大!
這個函數的時間復雜度是O(n),比排序的O(nlogn)快多了!找中位數、找前K大元素都是它的拿手好戲。
5. std::partial_sum - 前綴和計算機
算前綴和還在手寫雙重循環?這個函數一步到位!
#include <numeric> // partial_sum在這里
vector<int> nums = {1, 2, 3, 4, 5};
vector<int> prefix_sum(nums.size());
std::partial_sum(nums.begin(), nums.end(), prefix_sum.begin());
// prefix_sum: [1, 3, 6, 10, 15]
for(int x : prefix_sum) {
cout << x << " ";
}
還能玩出花來:
vector<int> nums = {2, 3, 4, 5};
vector<int> products(nums.size());
std::partial_sum(nums.begin(), nums.end(), products.begin(),
std::multiplies<int>());
// products: [2, 6, 24, 120] (累乘)
6. std::rotate - 數組旋轉大師
左旋轉、右旋轉,一個函數全搞定!
#include <algorithm> // std::rotate在這里
vector<int> nums = {1, 2, 3, 4, 5, 6, 7};
// 向左旋轉3位
std::rotate(nums.begin(), nums.begin() + 3, nums.end());
// 結果:[4, 5, 6, 7, 1, 2, 3]
for(int x : nums) {
cout << x << " ";
}
字符串操作也超級方便:
string s = "abcdefg";
std::rotate(s.begin(), s.begin() + 2, s.end());
cout << s << endl; // 輸出:cdefgab
7. std::set_intersection - 求交集的專家
兩個有序數組求交集,不用自己寫雙指針了!
#include <algorithm> // std::set_intersection在這里
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b = {3, 4, 5, 6, 7};
vector<int> result;
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(result));
// result: [3, 4, 5]
cout << "交集: ";
for(int x : result) {
cout << x << " ";
}
還有set_union(并集)、set_difference(差集)等兄弟函數,處理集合運算簡直不要太爽!
8. std::lexicographical_compare - 字典序比較王者
真正的字典序比較,就像查字典一樣!比較兩個序列誰在"字典"里排在前面:
#include <algorithm> // std::lexicographical_compare在這里
vector<string> words1 = {"apple", "banana"};
vector<string> words2 = {"apple", "cherry"};
bool result = std::lexicographical_compare(
words1.begin(), words1.end(),
words2.begin(), words2.end()
);
cout << (result ? "words1 在字典中排在前面" : "words2 在字典中排在前面") << endl;
// 輸出:words1 在字典中排在前面 (因為 banana < cherry)
字符串比較也很直觀:
string s1 = "abc";
string s2 = "abd";
bool result = std::lexicographical_compare(s1.begin(), s1.end(),
s2.begin(), s2.end());
cout << (result ? "s1 < s2" : "s1 >= s2") << endl; // 輸出:s1 < s2
自定義比較規則,比如忽略大小寫:
string s1 = "Apple";
string s2 = "banana";
bool result = std::lexicographical_compare(
s1.begin(), s1.end(), s2.begin(), s2.end(),
[](char a, char b) {
return tolower(a) < tolower(b);
}
);
// 按忽略大小寫的字典序比較
9. std::inplace_merge - 原地歸并排序
有兩個已排序的數組段,想合并成一個?這個函數就是為你準備的!
#include <algorithm> // std::inplace_merge在這里
vector<int> nums = {1, 3, 5, 2, 4, 6};
// 前半部分[1,3,5]已排序,后半部分[2,4,6]已排序
std::inplace_merge(nums.begin(), nums.begin() + 3, nums.end());
// 結果:[1, 2, 3, 4, 5, 6]
for(int x : nums) {
cout << x << " ";
}
內存效率超高,時間復雜度O(n),歸并排序的核心就是它!
10. std::sample - 隨機采樣專家
從大數據集里隨機選幾個樣本?這個函數幫你搞定!
#include <random>
#include <algorithm> // std::sample在這里
vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> samples;
std::random_device rd;
std::mt19937 gen(rd());
std::sample(data.begin(), data.end(), std::back_inserter(samples),
3, gen); // 隨機選3個
cout << "隨機樣本: ";
for(int x : samples) {
cout << x << " ";
}
// 可能輸出:隨機樣本: 2 7 9
做數據分析、機器學習預處理的時候超級有用!
11. std::partition - 數據分類專家
想把數組按條件分成兩部分?比如把奇數放前面,偶數放后面?
#include <algorithm> // std::partition在這里
vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};
auto it = std::partition(nums.begin(), nums.end(),
[](int x) { return x % 2 == 1; });
cout << "奇數部分: ";
for(auto i = nums.begin(); i != it; ++i) {
cout << *i << " "; // 輸出:1 9 3 7 5
}
cout << "\n偶數部分: ";
for(auto i = it; i != nums.end(); ++i) {
cout << *i << " "; // 輸出:6 4 8 2
}
還有個更穩定的版本std::stable_partition,能保持原有的相對順序!
12. std::mismatch - 找不同點的偵探
比較兩個序列,找出第一個不同的位置:
#include <algorithm> // std::mismatch在這里
string s1 = "programming";
string s2 = "programing"; // 少了一個m
auto result = std::mismatch(s1.begin(), s1.end(), s2.begin());
if(result.first != s1.end()) {
cout << "第一個不同的位置: " << (result.first - s1.begin()) << endl;
cout << "字符分別是: '" << *result.first << "' 和 '" << *result.second << "'" << endl;
}
// 輸出:第一個不同的位置: 7
// 字符分別是: 'm' 和 'i'
做字符串diff、數據校驗的時候特別有用!
13. std::equal_range - 二分查找三件套
在有序數組里查找某個值的所有出現位置,一次性給你上下邊界:
#include <algorithm> // std::equal_range在這里
vector<int> nums = {1, 2, 2, 2, 3, 4, 5};
auto range = std::equal_range(nums.begin(), nums.end(), 2);
cout << "數字2的范圍: [" << (range.first - nums.begin())
<< ", " << (range.second - nums.begin()) << ")" << endl;
// 輸出:數字2的范圍: [1, 4)
cout << "數字2出現了 " << (range.second - range.first) << " 次" << endl;
// 輸出:數字2出現了 3 次
比lower_bound和upper_bound分別調用要高效!
14. std::clamp - 數值限制器
把數值限制在某個范圍內,超出就截斷:
#include <algorithm>
int score = 150;
int clamped = std::clamp(score, 0, 100); // 限制在0-100之間
cout << "原始分數: " << score << ", 限制后: " << clamped << endl;
// 輸出:原始分數: 150, 限制后: 100
double temperature = -10.5;
double safe_temp = std::clamp(temperature, -5.0, 35.0);
cout << "溫度調節: " << temperature << " -> " << safe_temp << endl;
// 輸出:溫度調節: -10.5 -> -5
游戲開發、數據處理時經常用到!
15. std::generate_n - 批量生成器
想生成N個隨機數?想批量初始化數據?這個函數幫你搞定:
#include <random>
#include <algorithm>
vector<int> random_nums(10);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1, 100);
std::generate_n(random_nums.begin(), 10, [&]() { return dis(gen); });
cout << "10個隨機數: ";
for(int x : random_nums) {
cout << x << " ";
}
還能生成其他類型的數據:
vector<string> passwords(5);
std::generate_n(passwords.begin(), 5, []() {
return "pwd_" + to_string(rand() % 1000);
});
// 生成5個隨機密碼
16. std::search_n - 連續元素搜索專家
找連續出現N次的元素?比如找股票連續3天漲停,或者找字符串里連續的空格?
#include <algorithm> // std::search_n在這里
vector<int> stock_changes = {1, -1, 1, 1, 1, -1, 1, 1}; // 1表示漲,-1表示跌
auto it = std::search_n(stock_changes.begin(), stock_changes.end(), 3, 1);
if(it != stock_changes.end()) {
cout << "找到連續3天上漲,開始位置: " << (it - stock_changes.begin()) << endl;
// 輸出:找到連續3天上漲,開始位置: 2
}
// 字符串中找連續空格
string text = "hello world"; // 4個連續空格
auto pos = std::search_n(text.begin(), text.end(), 3, ' ');
if(pos != text.end()) {
cout << "找到3個連續空格,位置: " << (pos - text.begin()) << endl;
}
還能自定義條件:
vector<int> temps = {25, 26, 27, 28, 29, 20, 15}; // 溫度數據
// 找連續3天溫度都超過25度
auto it = std::search_n(temps.begin(), temps.end(), 3, 25,
[](int actual, int threshold) { return actual > threshold; });
if(it != temps.end()) {
cout << "找到連續3天超過25度,開始位置: " << (it - temps.begin()) << endl;
// 輸出:找到連續3天超過25度,開始位置: 0 (25,26,27都>25)
}
17. std::partial_sort - 部分排序高手
只想要前K個最大/最小值?不用全排序!這個函數比sort快多了:
#include <algorithm>
vector<int> scores = {85, 92, 78, 96, 88, 73, 91, 89, 94, 77};
// 只要前3名,不用全部排序
std::partial_sort(scores.begin(), scores.begin() + 3, scores.end(), std::greater<int>());
cout << "前3名分數: ";
for(int i = 0; i < 3; i++) {
cout << scores[i] << " ";
}
// 輸出:前3名分數: 96 94 92
// 后面的元素順序是未定義的,但前3個一定是最大的3個!
找中位數也超級高效:
vector<double> data = {3.2, 1.5, 4.8, 2.1, 5.7, 1.2, 6.3, 2.9};
int mid = data.size() / 2;
std::partial_sort(data.begin(), data.begin() + mid + 1, data.end());
cout << "中位數: " << data[mid] << endl;
性能對比:
- sort全排序:O(n log n)
- partial_sort前K個:O(n log k) - 當k很小時,快很多!
18. std::unique_copy - 去重復制專家
想去重但不想修改原數組?這個函數幫你復制一份去重后的數據:
#include <algorithm>
vector<int> nums = {1, 1, 2, 2, 2, 3, 3, 4, 5, 5};
vector<int> unique_nums;
// 前提:原數組要先排序!
std::unique_copy(nums.begin(), nums.end(), std::back_inserter(unique_nums));
cout << "原數組: ";
for(int x : nums) cout << x << " ";
cout << "\n去重后: ";
for(int x : unique_nums) cout << x << " ";
// 輸出:去重后: 1 2 3 4 5
處理字符串也很方便:
string text = "aabbccddee";
string result;
std::unique_copy(text.begin(), text.end(), std::back_inserter(result));
cout << "去重字符串: " << result << endl; // 輸出:abcde
高級用法 - 自定義去重條件:
vector<string> words = {"hello", "HELLO", "world", "WORLD", "test"};
vector<string> unique_words;
// 按忽略大小寫去重
std::sort(words.begin(), words.end(), [](conststring& a, conststring& b) {
string lower_a = a, lower_b = b;
transform(lower_a.begin(), lower_a.end(), lower_a.begin(), ::tolower);
transform(lower_b.begin(), lower_b.end(), lower_b.begin(), ::tolower);
return lower_a < lower_b;
});
for(string x : words) cout << x << " ";
std::unique_copy(words.begin(), words.end(), std::back_inserter(unique_words),
[](conststring& a, conststring& b) {
string lower_a = a, lower_b = b;
transform(lower_a.begin(), lower_a.end(), lower_a.begin(), ::tolower);
transform(lower_b.begin(), lower_b.end(), lower_b.begin(), ::tolower);
return lower_a == lower_b;
});
for(string x : unique_words) cout << x << " ";
// 結果:保留一個hello和一個world
使用技巧:
- unique_copy要求輸入已排序
- 用back_inserter可以自動擴容目標容器
- 可以配合自定義比較函數做復雜去重
獎勵關:幾個超級實用的組合技
技巧1:快速去重并保持順序
vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6, 5};
// 方法:先排序,再unique
sort(nums.begin(), nums.end());
nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
// 結果:[1, 2, 3, 4, 5, 6, 9]
技巧2:統計滿足條件的元素個數
vector<int> scores = {85, 92, 78, 96, 88, 73, 91};
int high_scores = std::count_if(scores.begin(), scores.end(),
[](int x) { return x >= 90; });
cout << "高分人數: " << high_scores << endl; // 輸出:3
技巧3:檢查是否全部/任意元素滿足條件
vector<int> ages = {18, 19, 20, 21, 22};
bool all_adult = std::all_of(ages.begin(), ages.end(),
[](int x) { return x >= 18; });
cout << "都是成年人: " << (all_adult ? "是" : "否") << endl;
bool has_senior = std::any_of(ages.begin(), ages.end(),
[](int x) { return x >= 65; });
cout << "有老年人: " << (has_senior ? "是" : "否") << endl;
總結:從此告別"笨代碼"
這18個STL算法就像武功秘籍一樣,每一個都能讓你的代碼變得更短、更快、更優雅。關鍵是它們都經過了高度優化,性能比你手寫的循環要好得多。
記住幾個使用技巧:
- 頭文件要記牢 - <algorithm>、<numeric>、<iterator>是三大寶庫
- 善用lambda表達式 - 自定義比較條件超級靈活,比寫函數對象簡單多了
- 迭代器是萬金油 - 不只是vector,string、deque、list、set都能用
- 性能優先選擇 - nth_element比排序快,partial_sort比全排序快,根據需求選最合適的
- 組合使用威力大 - 比如sort + unique去重,partition + sort分類排序
- 前置條件要注意 - 很多算法要求輸入已排序(如unique、binary_search)
- 善用插入迭代器 - back_inserter、front_inserter讓容器自動擴容
- 自定義比較函數 - 大部分算法都支持傳入比較函數,讓功能更強大
下次寫代碼之前,先問問自己:"有沒有STL算法能幫我?"說不定就能找到現成的輪子,何必自己造呢?
最后送你一句話:好的程序員不是寫代碼最多的,而是寫代碼最少的。用好STL,讓你的代碼簡潔如詩!