使用 std::unique
因爲要把vector中的相同相鄰元素去掉,便想到了算法unique。
std::unique(intvect.begin(), intvect.end());
可是調用了以後發現程序依然和調用前相同的行爲,感覺很奇怪。調試發現vector的大小根本沒變,也就是說相同元素沒被刪除。
我在visual c++ 2005 下工作,一直使用的是Microsoft提供的stl庫。unique的代碼…/Microsoft Visual Studio 8/VC/include/algorithm中,代碼不多:
// TEMPLATE FUNCTION unique
template<class _FwdIt> inline
_FwdIt _Unique(_FwdIt _First, _FwdIt _Last)
{ // remove each matching previous
_DEBUG_RANGE(_First, _Last);
for (_FwdIt _Firstb; (_Firstb = _First) != _Last && ++_First != _Last; )
if (*_Firstb == *_First)
{ // copy down
for (; ++_First != _Last; )
if (!(*_Firstb == *_First))
*++_Firstb = *_First;
return (++_Firstb);
}
return (_Last);
}
template<class _FwdIt> inline
_FwdIt unique(_FwdIt _First, _FwdIt _Last)
{ // remove each matching previous
_ASSIGN_FROM_BASE(_Last,
_Unique(_CHECKED_BASE(_First), _CHECKED_BASE(_Last)));
return (_Last);
}
Unique只是把不同的相鄰元素copy到了前面,返回的iterator是沒有相同相鄰元素的下一個迭代位置。
_Firstb指向的是結果的末尾元素,傳入參數_First用來遞增遍歷元素,當它找到與_Firstb不相等的元素時,就把這個元素copy到_Firstb的下一個位置。當然,_Firstb的下一個元素將被覆蓋,它被丟棄了。這是裏面的for循環所做的事。
外層的for循環只是保證當有相鄰相同元素時才進入到裏層for中進行copy工作,這是一種優化。雖然有兩層for,但算法的複雜度爲O(n)。
看如下的例子:
std::vector<int> intvect(3, 10);
intvect.insert(intvect.end(), 2, 11);
std::unique(intvect.begin(), intvect.end());
std::copy(intvect.begin(), intvect.end(), std::ostream_iterator<int>(std::cout, "/n"));
intvect中的值在unique之前是10,10,10,11,11
之後是10,11,10,11,11。
unique返回的iterator指向第三個元素,也就是正確結果(第二個)的下一個。intvect 在調用unique之後元素被改變了,不考慮順尋,它個元素和調用前也已經不相同了。所以在使用完unique之後要調用vector::erase將後面的元素刪除。
intvect.erase(std::unique(intvect.begin(), intvect.end()), intvect.end());
這樣寫,一切就正常了。
爲什麼unique不刪除已經沒用的元素?因爲它不會刪,它只是個template算法,它連傳入iterator的容器是什麼都不知道,當然也無法刪除了。
C++標準對此也沒多說,爲了方便我把標準有關unique的內容摘錄如下:
25.2.8 Unique [lib.alg.unique]
template<class ForwardIterator>
ForwardIterator unique(ForwardIterator first, ForwardIterator last);
template<class ForwardIterator, class BinaryPredicate>
ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
1 Effects: Eliminates all but the first element from every consecutive group of equal elements referred to by the iterator i in the range [first, last) for which the following corresponding conditions hold:
*i == *(i - 1) or pred(*i, *(i - 1)) != false
2 Returns: The end of the resulting range.
3 Complexity: If the range (last - first) is not empty, exactly (last - first) - 1 applications of the corresponding predicate, otherwise no applications of the predicate.
template<class InputIterator, class OutputIterator>
OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result);
template<class InputIterator, class OutputIterator, class BinaryPredicate>
OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate pred);
4 Requires: The ranges [first, last) and [result, result+(last-first)) shall not overlap.
5 Effects: Copies only the first element from every consecutive group of equal elements referred to by the iterator i in the range [first, last) for which the following corresponding conditions hold:
*i == *(i - 1) or pred(*i, *(i - 1)) != false
6 Returns: The end of the resulting range.
7 Complexity: Exactly last - first applications of the corresponding predicate.
如標準所示,unique算法還有另外幾種類型,但實現都大同小異。
在標準下,stl的實現各異。Reading The Fucking Code!