[Happy Coding] 一段調用STL算法的程序代碼的效率分析

已知一個STD::SET,想要根據一個predicate來從中去除所有的滿足predicate(返回true)的元素。

我們首先想到的是STL提供的remove_if函數。
下面我們來看看這個函數是如何實現的:

1133   template<typename _ForwardIterator, typename _Predicate>
1134     _ForwardIterator
1135     remove_if(_ForwardIterator __first, _ForwardIterator __last,
1136           _Predicate __pred)
1137     {
1144
1145       __first = std::find_if(__first, __last, __pred);
1146       _ForwardIterator __i = __first;
1147       return __first == __last ? __first
1148                    : std::remove_copy_if(++__i, __last,
1149                              __first, __pred);
1150     }

 這個函數首先調用std::find_if來幹活,下面來看看std::find_if的實現過程:
std::find_if提供了很多重載版本,主要根據iterator的類型:
由於我們的std::set不是randomIterator,所以就會調用到下面的函數:
182   template<typename _InputIterator, typename _Predicate>
183     inline _InputIterator
184     find_if(_InputIterator __first, _InputIterator __last,
185         _Predicate __pred, input_iterator_tag)
186     {
187       while (__first != __last && !__pred(*__first))
188     ++__first;
189       return __first;
190     }

可以看到,這個函數迭代的查看每一個元素,判斷是否滿足predicate的要求。

我們來看看std::remove_copy_if函數。
當找到第一個滿足條件的元素之後,便從這個元素開始來調用remove_copy_if函數。

1059   template<typename _InputIterator, typename _OutputIterator,
1060        typename _Predicate>
1061     _OutputIterator
1062     remove_copy_if(_InputIterator __first, _InputIterator __last,
1063            _OutputIterator __result, _Predicate __pred)
1064     {
1072
1073       for ( ; __first != __last; ++__first)
1074     if (!__pred(*__first))
1075       {
1076         *__result = *__first;
1077         ++__result;
1078       }
1079       return __result;
1080     }
這個函數也是迭代的查看每一個元素,判斷是否滿足要求,不滿足條件的(!__pred(*__first)),將它COPY到輸出容器中。
請注意這個操作,按照 remove_if傳遞進來的輸入輸出迭代器,來自於同一個容器,
a) 如果後面沒有任何元素滿足predicate的條件,那麼這個函數將僅僅只是LOOP那些元素而已。
b) 如果後面的元素有一些滿足predicate的條件,那麼這個賦值操作是否做什麼事情,將取決於容器內元素提供的賦值操作符實現。如果元素的賦值操作符實現進行了自賦值檢查(什麼都不做),那麼這個賦值將不會花銷任何時間,否則對於自定義的類型可能就會有較大的開銷。

所以用std::remove_if來針對std::set進行操作,效率比較低效,原因在於stl提供的算法都無法知曉具體容器的特點,它們只能根據容器提供的迭代接口來遍歷容器,進行操作。所以更有效的方法就是調用這些容器自己提供的接口,例如std::set::find

1095   template<typename _Key, typename _Val, typename _KeyOfValue,
1096            typename _Compare, typename _Alloc>
1097     typename _Rb_tree<_Key,_Val,_KeyOfValue,_Compare,_Alloc>::iterator
1098     _Rb_tree<_Key,_Val,_KeyOfValue,_Compare,_Alloc>::find(const _Key& __k)
1099     {
1100       _Link_type __x = _M_begin(); // Current node.
1101       _Link_type __y = _M_end(); // Last node which is not less than __k.
1102
1103       while (__x != 0)
1104     if (!_M_impl._M_key_compare(_S_key(__x), __k))
1105       __y = __x, __x = _S_left(__x);
1106     else
1107       __x = _S_right(__x);
1108
1109       iterator __j = iterator(__y);
1110       return (__j == end()
1111       || _M_impl._M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;
1112     }

它基於內部的二叉樹結構來快速的查找滿足條件的元素。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章