使用 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!