C++ STL 泛型演算法 (<algorithm>) 是運算在迭代器之上的介面,不直接操作容器,卻能改變元素值、移動元素位置。本文系統梳理基礎演算法分類、自訂操作 (函數物件/Lambda/Bind)、特殊迭代器、五大迭代器類別,以及 list 專屬演算法,並附完整程式碼範例。
不修改元素,僅讀取區間內資料:
std::vector<int> v = {1,2,3,4,5,4,3,2,1};
auto it = std::find(v.begin(), v.end(), 2); // 找第一個 2
auto cnt = std::count(v.begin(), v.end(), 2); // 計數 2 出現次數 (2)
auto sum = std::accumulate(v.begin(), v.end(), 0); // 求和 (25)
std::string s1 = "Summer love C++";
auto ssum = std::accumulate(s1.begin(), s1.end(), std::string("Hello:")); // 字串串接
bool eq = std::equal(s1.begin(), s1.end()-3, s2.begin()); // 比較前段是否相等
修改區間元素值,或透過插入迭代器新增元素:
std::fill(v.begin(), v.end(), 0); // 全部設 0 std::fill_n(v.begin(), 3, -1); // 前 3 個設 -1 std::fill_n(std::back_inserter(v), 5, 1); // 尾部插入 5 個 1 (v 變大) std::replace(v.begin(), v.end(), 0, 1); // 0→1 std::vector<int> v2; std::replace_copy(v.begin(), v.end(), std::back_inserter(v2), -1, 1); // 複製並替換,v 不變
改變元素順序:
std::sort(v.begin(), v.end()); // 排序 auto new_end = std::unique(v.begin(), v.end()); // 移除相鄰重複,回傳新邏輯尾端 v.erase(new_end, v.end()); // 真正刪除多餘元素
bool isSmaller(int a, int b) { return a%10 < b%10; } // 按個位數排序
std::sort(v.begin(), v.end(), isSmaller);
stable_sort 保持相等元素相對順序std::sort(v.begin(), v.end()); // 先按整體大小排 (十位數有序) std::stable_sort(v.begin(), v.end(), isSmaller); // 再按個位數穩定排序
語法:[捕獲列表](參數列表) mutable -> 回傳型別 { 函數體 }
// 簡化排序謂詞
std::stable_sort(v.begin(), v.end(), [](int a, int b){ return a%10 < b%10; });
// 捕獲區域變數 n
int n = 5;
auto pos = std::find_if(v.begin(), v.end(), [n](int i){ return i%10 >= n; });
// for_each 區間處理
std::for_each(v.begin(), pos, [](int i){ std::cout << (i/10)*10 << ' '; });
std::for_each(pos, v.end(), [](int i){ std::cout << (i/10+1)*10 << ' '; });
// 捕獲模式
int v1=1, v2=2;
auto f_val = [v1]{ return v1; }; // 值捕獲
auto f_ref = [&v2]{ return v2; }; // 參考捕獲
auto f_allv = [=]{ return v1+v2; }; // 全值捕獲
auto f_allr = [&]{ return v1+v2; }; // 全參考捕獲
auto f_mix = [=,&v2]{ return v1+v2; }; // 混合:v1 值、v2 參考
auto f_mut = [v1]() mutable { return ++v1; }; // mutable 允許修改值捕獲變數
// 複雜邏輯需顯式指定回傳型別
auto f_ret = [v1]() mutable -> int { ++v1; return v1; };
std::bind 參數綁定 (C++11)將多參數函數綁定為一元謂詞,配合佔位符 _1, _2... (std::placeholders):
bool checkNumber(int i, int n) { return i%10 >= n; }
using namespace std::placeholders;
auto pos = std::find_if(v.begin(), v.end(), std::bind(checkNumber, _1, n));
// 等價於 Lambda: [n](int i){ return i%10 >= n; }
// 交換參數順序
std::sort(v.begin(), v.end(), std::bind(isSmaller, _2, _1)); // 遞減
// 參考參數需用 ref/cref 保護
int x = 5; int& rx = x;
auto pos2 = std::find_if(v.begin(), v.end(), std::bind(checkNumber, _1, std::ref(rx)));
適配器,將演算法寫入操作轉為容器插入操作:
std::list<int> l = {1,2,3,4,5}, l1, l2, l3;
std::copy(l.begin(), l.end(), std::front_inserter(l1)); // 頭插:5 4 3 2 1 (反序)
std::copy(l.begin(), l.end(), std::back_inserter(l2)); // 尾插:1 2 3 4 5
std::copy(l.begin(), l.end(), std::inserter(l3, l3.begin())); // 指定位置插入
// 輸入流迭代器:從 cin 讀 int 求和 std::istream_iterator<int> in(std::cin), eof; std::cout << std::accumulate(in, eof, 0) << '\n'; // 輸出流迭代器:將 vector 輸出到 cout std::ostream_iterator<int> out(std::cout, " "); std::copy(v.begin(), v.end(), out);
std::string s = "C++,Objective-C,Swift"; auto r_it = std::find(s.crbegin(), s.crend(), ','); // 反向找逗號 std::cout << std::string(s.crbegin(), r_it) << '\n'; // tfiwS (反序) std::cout << std::string(r_it.base(), s.cend()) << '\n'; // Swift (base() 轉正向)
| 類別 | 能力 | 代表演算法 | 典型容器/型別 |
|---|---|---|---|
| Input Iterator | 唯讀、單遍、只能 ++ | find, accumulate | istream_iterator |
| Output Iterator | 唯寫、單遍、只能 ++ | copy (dest), ostream_iterator | - |
| Forward Iterator | 讀寫、多遍、只能 ++ | replace | forward_list |
| Bidirectional Iterator | 讀寫、多遍、++/-- | reverse | list, set, map |
| Random Access Iterator | 讀寫、多遍、+, -, [], <, > | sort | vector, deque, string, array, pointer |
原則:演算法要求最弱迭代器類別,容器提供最強類別即可通用。
list 專屬演算法list 為雙向鏈表,無隨機存取迭代器,無法使用 sort 等通用演算法,故提供成員函數版本:
std::list<int> l = {15,24,33,42,51}, l2 = {41,32,23,14};
l.merge(l2); // 合併兩有序 list,l2 變空,元素移動不複製
l.sort(isSmaller); // 成員 sort,O(N log N)
l.unique(isEqual); // 移除相鄰等價元素 (需先排序)
// splice: 將 l2 元素移動到 l 指定位置,l2 對應元素被移除 (O(1))
l.splice(l.end(), l2); // 移動全部
l.splice(l.end(), l2, l2.begin()); // 移動單一元素
l.splice(l.end(), l2, l2.begin(), l2.end()); // 移動區間 (等價全部)
關鍵差異:list 專屬版本修改底層容器結構 (節點移動),通用版本僅交換/賦值元素值。merge 後 l2 變空,但節點記憶體未釋放,僅改變鏈接關係。
vector + sort,或 list::sort。list + splice。sort + unique + erase (vector) 或 list::sort + list::unique。istream_iterator/ostream_iterator 配合 accumulate/copy。精通泛型演算法與迭代器體系,是寫出高效、泛用、現代化 C++ 程式碼的關鍵鑰匙。