首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 泛型演算法精要:從只讀、寫入、重排到 Lambda、Bind 與迭代器分類完全掌握

     編輯:關於C++

C++ STL 泛型演算法 (<algorithm>) 是運算在迭代器之上的介面,不直接操作容器,卻能改變元素值、移動元素位置。本文系統梳理基礎演算法分類、自訂操作 (函數物件/Lambda/Bind)、特殊迭代器、五大迭代器類別,以及 list 專屬演算法,並附完整程式碼範例。

一、基礎演算法三大類

1. 只讀演算法

不修改元素,僅讀取區間內資料:

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()); // 比較前段是否相等

2. 寫入演算法

修改區間元素值,或透過插入迭代器新增元素:

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 不變

3. 重排演算法

改變元素順序:

std::sort(v.begin(), v.end());                 // 排序
auto new_end = std::unique(v.begin(), v.end()); // 移除相鄰重複,回傳新邏輯尾端
v.erase(new_end, v.end());                     // 真正刪除多餘元素

二、自訂操作:謂詞、Lambda 與 Bind

1. 函數指標/函數物件作謂詞

bool isSmaller(int a, int b) { return a%10 < b%10; } // 按個位數排序
std::sort(v.begin(), v.end(), isSmaller);

2. stable_sort 保持相等元素相對順序

std::sort(v.begin(), v.end());                 // 先按整體大小排 (十位數有序)
std::stable_sort(v.begin(), v.end(), isSmaller); // 再按個位數穩定排序

3. Lambda 表達式 (C++11)

語法:[捕獲列表](參數列表) 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; };

4. 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)));

三、特殊迭代器

1. 插入迭代器

適配器,將演算法寫入操作轉為容器插入操作:

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())); // 指定位置插入

2. I/O 流迭代器

// 輸入流迭代器:從 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);

3. 反向迭代器

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, accumulateistream_iterator
Output Iterator唯寫、單遍、只能 ++copy (dest), ostream_iterator-
Forward Iterator讀寫、多遍、只能 ++replaceforward_list
Bidirectional Iterator讀寫、多遍、++/--reverselist, set, map
Random Access Iterator讀寫、多遍、+, -, [], <, >sortvector, 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。
  • 自訂邏輯:優先 Lambda,複雜復用用 函數物件,參數綁定用 bind。

精通泛型演算法與迭代器體系,是寫出高效、泛用、現代化 C++ 程式碼的關鍵鑰匙。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved