从零开始学C++之STL(七):剩下5种算法代码分析与使用示例(remove 、rotate 、sort、lower_bound、accumulate)
一、移除性算法 (remove)
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | //?TEMPLATE?FUNCTION?remove_copy template?<?class?_InIt, ?????????class?_OutIt, ?????????class?_Ty?>?inline _OutIt?_Remove_copy(_InIt?_First,?_InIt?_Last, ????????????????????_OutIt?_Dest,?const?_Ty?&_Val,?_Range_checked_iterator_tag) { ????//?copy?omitting?each?matching?_Val ????_DEBUG_RANGE(_First,?_Last); ????_DEBUG_POINTER(_Dest); ????for?(;?_First?!=?_Last;?++_First) ????????if?(!(*_First?==?_Val)) ????????????*_Dest++?=?*_First; ????return?(_Dest); } template?<?class?_InIt, ?????????class?_OutIt, ?????????class?_Ty?>?inline _OutIt?unchecked_remove_copy(_InIt?_First,?_InIt?_Last, ?????????????????????????????_OutIt?_Dest,?const?_Ty?&_Val) { ????//?copy?omitting?each?matching?_Val ????return?_STD?_Remove_copy(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Dest,?_Val, ?????????????????????????????_STD?_Range_checked_iterator_tag()); } //?TEMPLATE?FUNCTION?remove template?<?class?_FwdIt, ?????????class?_Ty?>?inline _FwdIt?remove(_FwdIt?_First,?_FwdIt?_Last,?const?_Ty?&_Val) { ????//?remove?each?matching?_Val ????_First?=?find(_First,?_Last,?_Val); ????if?(_First?==?_Last) ????????return?(_First);????//?empty?sequence,?all?done ????else ????{ ????????//?nonempty?sequence,?worth?doing ????????_FwdIt?_First1?=?_First; ????????return?(_STDEXT?unchecked_remove_copy(++_First1,?_Last,?_First,?_Val)); ????} } |
如下圖所示:
假設現在想要remove 的元素是3,則傳入到?_Remove_copy 函數的3個參數如上圖第一行所示,Val 即3。
接著遍歷First ~ Last 區間的元素,將非移除元素拷貝到前面,覆蓋前面的元素,最后的指向如圖,函數返回的是Dest 位置,如下代
碼所示:
for?(;?_First?!=?_Last;?++_First)
?if?(!(*_First?==?_Val))
????????????*_Dest++?=?*_First;
由上圖可看出移除性算法并沒有改變元素的個數,如果要真正刪除,可以將remove 的返回值傳入erase 進行刪除,如:
v.erase(remove(v.begin(), v.end(), 3), v.end()); 即會將后面兩個元素4 和 5 刪除掉。
在這里順便提一下,erase 會使當前迭代器失效,但可以返回下一個迭代器,故如果需要在遍歷中刪除,下面的做法才是正確的:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include?<iostream> #include?<vector> using?namespace?std; int?main(void) { ????int?a[]?=?{3,?1,?2,?3,?4}; ????vector<int>?v(a,?a?+?5); ????//for?(vector<int>::iterator?it=v.begin();?it!=v.end();?++it) ????//{ ????//??if?(*it?==?3) ????//??????v.erase(it);??????????ERROR! ????//??else ????//??????cout<<*it<<'?'; ????//} ????for?(vector<int>::iterator?it?=?v.begin();?it?!=?v.end();) ????{ ????????if?(*it?==?3) ????????????it?=?v.erase(it); ????????else ????????{ ????????????cout?<<?*it?<<?'?'; ????????????++it; ????????} ????} ????cout?<<?endl; ????return?0; } |
示例代碼1:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include?<iostream> #include?<vector> #include?<list> #include?<algorithm> using?namespace?std; void?print_element(int?n) { ????cout?<<?n?<<?'?'; } int?main(void) { ????int?a[]?=?{?1,?3,?2,?3,?4,?5?}; ????vector<int>?v(a,?a?+?6); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????/*remove(v.begin(),?v.end(),?3); ????for_each(v.begin(),?v.end(),?print_element); ????cout<<endl;*/ ????v.erase(remove(v.begin(),?v.end(),?3),?v.end()); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????return?0; } |
二、變序性算法(?rotate)
C++ Code?
| 1 2 3 4 5 6 7 | template<class?_FwdIt>?inline void?rotate(_FwdIt?_First,?_FwdIt?_Mid,?_FwdIt?_Last) { ????//?rotate?[_First,?_Last) ????if?(_First?!=?_Mid?&&?_Mid?!=?_Last) ????????_Rotate(_CHECKED_BASE(_First),?_CHECKED_BASE(_Mid),?_CHECKED_BASE(_Last),?_Iter_cat(_First)); } |
rotate 調用了_Rotate,實際上_Rotate 繼續調用了某個函數,內部實現代碼比較長,而且不容易看懂,這邊可以看一下簡易的等價
版本實現,來自這里,如下:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 | template?<class?ForwardIterator> void?rotate?(ForwardIterator?first,?ForwardIterator?middle, ?????????????ForwardIterator?last) { ????ForwardIterator?next?=?middle; ????while?(first?!=?next) ????{ ????????swap?(*first++,?*next++); ????????if?(next?==?last)?next?=?middle; ????????else?if?(first?==?middle)?middle?=?next; ????} } |
假設一個容器有 1 2 3 4 5 6 六個元素,現在想把 1 2 放到后面去,可以這樣寫?rotate(v.begin(), v.begin()+2, v.end()); ?如下圖所示:
即將first 與 next 對應的元素互換且不斷向前推進,直到first == next 為止。
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | template<class?_RanIt>?inline void?sort(_RanIt?_First,?_RanIt?_Last) { ????//?order?[_First,?_Last),?using?operator< ????_DEBUG_RANGE(_First,?_Last); ????std::_Sort(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Last?-?_First); } template?<?class?_RanIt, ?????????class?_Pr?>?inline void?sort(_RanIt?_First,?_RanIt?_Last,?_Pr?_Pred) { ????//?order?[_First,?_Last),?using?_Pred ????_DEBUG_RANGE(_First,?_Last); ????_DEBUG_POINTER(_Pred); ????std::_Sort(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Last?-?_First,?_Pred); } |
sort 重載了兩個版本,第一個版本只有2個參數,默認按從小到大排序(using?operator<);第二個版本有三個參數,即可以自定義比較邏輯
(_Pred)。它們都調用了標準庫的std::_Sort, 跟蹤進去發現比較復雜,在_Sort 內會根據一些條件選擇不同的排序方式,如標準庫的堆排序,合并
排序,插入排序等等。站在使用的角度看,沒必要去深究,但如果是想學習相關的排序,那是很好的資源。
示例代碼2:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include?<iostream> #include?<vector> #include?<list> #include?<algorithm> using?namespace?std; void?print_element(int?n) { ????cout?<<?n?<<?'?'; } bool?my_greater(int?a,?int?b) { ????return?a?>?b; } int?main(void) { ????int?a[]?=?{?1,?2,?3,?4,?5,?6?}; ????vector<int>?v(a,?a?+?6); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????rotate(v.begin(),?v.begin()?+?2,?v.end()); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????sort(v.begin(),?v.end()); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????sort(v.begin(),?v.end(),?my_greater); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????return?0; } |
此外,sort 有個坑,如果你自己傳遞比較邏輯,需要注意,如下:
四、已序區間算法 (lower_bound 、upper_bound)
使用這些算法的前提是區間已經是有序的。
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //?TEMPLATE?FUNCTION?lower_bound template?<?class?_FwdIt, ?????????class?_Ty, ?????????class?_Diff?>?inline _FwdIt?_Lower_bound(_FwdIt?_First,?_FwdIt?_Last,?const?_Ty?&_Val,?_Diff?*) { ????//?find?first?element?not?before?_Val,?using?operator< ????_DEBUG_ORDER_SINGLE(_First,?_Last,?true); ????_Diff?_Count?=?0; ????_Distance(_First,?_Last,?_Count); ????for?(;?0?<?_Count;?) ????{ ????????//?divide?and?conquer,?find?half?that?contains?answer ????????_Diff?_Count2?=?_Count?/?2; ????????_FwdIt?_Mid?=?_First; ????????std::advance(_Mid,?_Count2); ????????_DEBUG_ORDER_SINGLE(_Mid,?_Last,?false); ????????if?(_DEBUG_LT(*_Mid,?_Val)) ????????????_First?=?++_Mid,?_Count?-=?_Count2?+?1; ????????else ????????????_Count?=?_Count2; ????} ????return?(_First); } template?<?class?_FwdIt, ?????????class?_Ty?>?inline _FwdIt?lower_bound(_FwdIt?_First,?_FwdIt?_Last,?const?_Ty?&_Val) { ????//?find?first?element?not?before?_Val,?using?operator< ????_ASSIGN_FROM_BASE(_First, ??????????????????????_Lower_bound(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Val,?_Dist_type(_First))); ????return?_First; } |
lower_bound() 返回第一個“大于等于給定值" 的元素位置,其實還重載了另一個low_bound 版本,如下:
C++ Code?
| 1 2 3 4 5 6 7 | //?TEMPLATE?FUNCTION?lower_bound?WITH?PRED template?<?class?_FwdIt, ?????????class?_Ty, ?????????class?_Diff, ?????????class?_Pr?>?inline _FwdIt?_Lower_bound(_FwdIt?_First,?_FwdIt?_Last, ????????????????????const?_Ty?&_Val,?_Pr?_Pred,?_Diff?*) |
也就是可以自定義比較邏輯,需要注意的是如果使用這個版本,那么區間應該本來就是按comp 方法排序的,如下面這句話:
The elements are compared using?operator<?for the first version, and?comp?for the second. The elements in the range shall already
?be?sorted?according to this same criterion (operator<?or?comp), or at least?partitioned?with respect to?val.
由于是已序區間,所以函數內用的是二分查找,而兩個版本主要的代碼不同在于:
_DEBUG_LT(*_Mid,?_Val)
_DEBUG_LT_PRED(_Pred, *_Mid, _Val)
upper_bound 與 lower_bound 類似,不過返回的是第一個”大于給定值“ 的元素位置。
示例代碼3:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include?<iostream> #include?<vector> #include?<list> #include?<algorithm> using?namespace?std; void?print_element(int?n) { ????cout?<<?n?<<?'?'; } int?main(void) { ????int?a[]?=?{?1,?10,?10,?14,?15,?16?}; ????vector<int>?v(a,?a?+?6); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????vector<int>::iterator?it; ????it?=?lower_bound(v.begin(),?v.end(),?10); ????if?(it?!=?v.end()) ????{ ????????cout?<<?it?-?v.begin()?<<?endl; ????} ????it?=?upper_bound(v.begin(),?v.end(),?10); ????if?(it?!=?v.end()) ????{ ????????cout?<<?it?-?v.begin()?<<?endl; ????} ????return?0; } |
五、數值算法(accumulate)
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //?TEMPLATE?FUNCTION?accumulate template?<?class?_InIt, ?????????class?_Ty?>?inline _Ty?_Accumulate(_InIt?_First,?_InIt?_Last,?_Ty?_Val) { ????//?return?sum?of?_Val?and?all?in?[_First,?_Last) ????_DEBUG_RANGE(_First,?_Last); ????for?(;?_First?!=?_Last;?++_First) ????????_Val?=?_Val?+?*_First; ????return?(_Val); } template?<?class?_InIt, ?????????class?_Ty?>?inline _Ty?accumulate(_InIt?_First,?_InIt?_Last,?_Ty?_Val) { ????//?return?sum?of?_Val?and?all?in?[_First,?_Last) ????return?_Accumulate(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Val); } //?TEMPLATE?FUNCTION?accumulate?WITH?BINOP template?<?class?_InIt, ?????????class?_Ty, ?????????class?_Fn2?>?inline _Ty?_Accumulate(_InIt?_First,?_InIt?_Last,?_Ty?_Val,?_Fn2?_Func) { ????//?return?sum?of?_Val?and?all?in?[_First,?_Last),?using?_Func ????_DEBUG_RANGE(_First,?_Last); ????_DEBUG_POINTER(_Func); ????for?(;?_First?!=?_Last;?++_First) ????????_Val?=?_Func(_Val,?*_First); ????return?(_Val); } template?<?class?_InIt, ?????????class?_Ty, ?????????class?_Fn2?>?inline _Ty?accumulate(_InIt?_First,?_InIt?_Last,?_Ty?_Val,?_Fn2?_Func) { ????//?return?sum?of?_Val?and?all?in?[_First,?_Last),?using?_Func ????return?_Accumulate(_CHECKED_BASE(_First),?_CHECKED_BASE(_Last),?_Val,?_Func); } |
accumulate 重載了兩個版本,第一個版本實現的是累加,第二個版本帶_Func 參數,可以自定義計算,比如累乘等。代碼都比較好理解,就不贅述
了。看下面的示例代碼4:
C++ Code?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include?<iostream> #include?<vector> #include?<list> #include?<algorithm> #include?<numeric> using?namespace?std; void?print_element(int?n) { ????cout?<<?n?<<?'?'; } int?mult(int?a,?int?b) { ????return?a?*?b; } int?main(void) { ????int?a[]?=?{?1,?2,?3,?4,?5?}; ????vector<int>?v(a,?a?+?5); ????for_each(v.begin(),?v.end(),?print_element); ????cout?<<?endl; ????//?累加 ????cout?<<?accumulate(v.begin(),?v.end(),?0)?<<?endl; ????//?累乘 ????cout?<<?accumulate(v.begin(),?v.end(),?1,?mult)?<<?endl; ????return?0; } |
參考:
C++ primer 第四版
Effective C++ 3rd
C++編程規范
總結
以上是生活随笔為你收集整理的从零开始学C++之STL(七):剩下5种算法代码分析与使用示例(remove 、rotate 、sort、lower_bound、accumulate)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python场景动画_昨夜星辰多媒体情景
- 下一篇: 简历c语言项目,C/C++:如何介绍简历