我有一个包含很少非相邻重复项的向量。
作为一个简单的例子,请考虑:
2 1 6 1 4 6 2 1 1
我试图通过删除不相邻的重复项并保持元素的顺序来使这个vector
唯一。
结果将是:
2 1 6 4
我尝试的解决方案是:
手动复制消除:
Define a temporary vector TempVector.
for (each element in a vector)
{
if (the element does not exists in TempVector)
{
add to TempVector;
}
}
swap orginial vector with TempVector.
我的问题是:
是否有任何STL算法可以从向量中删除不相邻的重复项?它的复杂性是什么?
答案 0 :(得分:13)
我想你会这样做:
我会在向量上使用两个迭代器:
第一个读取数据并将其插入临时集。
当读取数据不在集合中时,将其从第一个迭代器复制到第二个迭代器并递增它。
最后,您只将数据保留到第二个迭代器。
复杂度为O(n .log(n)),因为重复元素的查找使用集合,而不是向量。
#include <vector>
#include <set>
#include <iostream>
int main(int argc, char* argv[])
{
std::vector< int > k ;
k.push_back( 2 );
k.push_back( 1 );
k.push_back( 6 );
k.push_back( 1 );
k.push_back( 4 );
k.push_back( 6 );
k.push_back( 2 );
k.push_back( 1 );
k.push_back( 1 );
{
std::vector< int >::iterator r , w ;
std::set< int > tmpset ;
for( r = k.begin() , w = k.begin() ; r != k.end() ; ++r )
{
if( tmpset.insert( *r ).second )
{
*w++ = *r ;
}
}
k.erase( w , k.end() );
}
{
std::vector< int >::iterator r ;
for( r = k.begin() ; r != k.end() ; ++r )
{
std::cout << *r << std::endl ;
}
}
}
答案 1 :(得分:11)
如果不使用临时set
,可能会(可能)出现性能损失:
template<class Iterator>
Iterator Unique(Iterator first, Iterator last)
{
while (first != last)
{
Iterator next(first);
last = std::remove(++next, last, *first);
first = next;
}
return last;
}
用作:
vec.erase( Unique( vec.begin(), vec.end() ), vec.end() );
对于较小的数据集,实现的简单性和缺乏额外的分配可能会抵消使用额外set
的理论上更高的复杂性。但是,使用代表性输入进行测量是唯一可以确定的方法。
答案 2 :(得分:6)
您可以使用remove_copy_if
class NotSeen : public std::unary_function <int, bool>
{
public:
NotSeen (std::set<int> & seen) : m_seen (seen) { }
bool operator ()(int i) const {
return (m_seen.insert (i).second);
}
private:
std::set<int> & m_seen;
};
void removeDups (std::vector<int> const & iv, std::vector<int> & ov) {
std::set<int> seen;
std::remove_copy_if (iv.begin ()
, iv.end ()
, std::back_inserter (ov)
, NotSeen (seen));
}
这对算法的复杂性没有影响(即写入它也是O(n log n))。您可以使用unordered_set对此进行改进,或者如果值的范围足够小,您可以简单地使用数组或位阵。
答案 3 :(得分:6)
问题是“是否有任何STL算法......?它的复杂性是什么?”实现像std::unique
:
template <class FwdIterator>
inline FwdIterator stable_unique(FwdIterator first, FwdIterator last)
{
FwdIterator result = first;
std::unordered_set<typename FwdIterator::value_type> seen;
for (; first != last; ++first)
if (seen.insert(*first).second)
*result++ = *first;
return result;
}
所以这就是std::unique
的实施方式加上额外的一套。 unordered_set
应比常规set
快。删除的所有元素都与它们之前的元素相等(第一个元素被保留,因为我们无法统一到任何东西)。迭代器返回指向[first,last)
范围内的新结尾。
编辑:最后一句意味着容器本身不会被unique
修改。这可能令人困惑。以下示例实际上将容器缩减为统一集。
1: std::vector<int> v(3, 5);
2: v.resize(std::distance(v.begin(), unique(v.begin(), v.end())));
3: assert(v.size() == 1);
第1行创建了一个向量{ 5, 5, 5 }
。在第2行中,调用unique
返回第二个元素的迭代器,这是第一个不唯一的元素。因此,distance
返回1,resize
修剪矢量。
答案 4 :(得分:3)
没有STL算法可以保留序列的原始顺序。
您可以在向量中创建std::set
迭代器或索引,使用比较谓词来使用引用的数据而不是迭代器/索引来对事物进行排序。然后从集合中未引用的向量中删除所有内容。 (当然,您也可以使用另一个std::vector
迭代器/索引std::sort
和std::unique
,并将其作为参考,以便保留什么。)
答案 5 :(得分:3)
基于@fa的答案。它也可以使用STL算法std::stable_partition
重写:
struct dupChecker_ {
inline dupChecker_() : tmpSet() {}
inline bool operator()(int i) {
return tmpSet.insert(i).second;
}
private:
std::set<int> tmpSet;
};
k.erase(std::stable_partition(k.begin(), k.end(), dupChecker_()), k.end());
这样它更紧凑,我们不需要关心迭代器。
似乎甚至没有引入太多性能损失。我在我的项目中使用它,它经常需要处理复杂类型的非常大的向量,并没有真正的区别。
另一个不错的功能是,可以使用std::set<int, myCmp_> tmpSet;
调整唯一性。例如,在我的项目中忽略某些舍入错误。
答案 6 :(得分:2)
我的问题是:
是否有任何STL算法可以从向量中删除不相邻的重复项?它的复杂性是什么?
STL选项是您提到的选项:将项目放在std::set
中,或致电std::sort
,std::unique
并在容器上调用erase()
。这些都不符合您的要求“删除不相邻的重复项并保持元素的顺序。”
那么STL为什么不提供其他选择呢?没有标准库可以满足每个用户的需求。 STL的设计考虑因素包括“对于几乎所有用户来说都足够快”,“对几乎所有用户都有用”,“尽可能提供异常安全”(并且“对于标准委员会来说足够小”,因为Stepanov库最初写得更大,Stroustrup砍掉了2/3的东西。
我能想到的最简单的解决方案如下:
// Note: an STL-like method would be templatized and use iterators instead of
// hardcoding std::vector<int>
std::vector<int> stable_unique(const std::vector<int>& input)
{
std::vector<int> result;
result.reserve(input.size());
for (std::vector<int>::iterator itor = input.begin();
itor != input.end();
++itor)
if (std::find(result.begin(), result.end(), *itor) == result.end())
result.push_back(*itor);
return result;
}
此解决方案应为O(M * N),其中M是唯一元素的数量,N是元素的总数。
答案 7 :(得分:2)
John Torjo有一篇很好的文章以系统的方式处理这个问题。他提出的结果似乎比目前建议的任何解决方案更通用,更有效:
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-1052159.html
不幸的是,John的解决方案的完整代码似乎已不再可用,John没有回复可能的电子邮件。因此,我编写了自己的代码,这些代码基于类似于他的类似理由,但在某些细节上有意不同。如果您愿意,请随时与我联系(vschoech think-cell com)并讨论详细信息。
为了让代码为你编译,我添加了一些我经常使用的库。此外,我使用boost来创建更通用,更高效,更易读的代码,而不是使用普通的stl。
玩得开心!
#include <vector>
#include <functional>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/iterator/counting_iterator.hpp>
/////////////////////////////////////////////////////////////////////////////////////////////
// library stuff
template< class Rng, class Func >
Func for_each( Rng& rng, Func f ) {
return std::for_each( boost::begin(rng), boost::end(rng), f );
};
template< class Rng, class Pred >
Rng& sort( Rng& rng, Pred pred ) {
std::sort( boost::begin( rng ), boost::end( rng ), pred );
return rng; // to allow function chaining, similar to operator+= et al.
}
template< class T >
boost::iterator_range< boost::counting_iterator<T> > make_counting_range( T const& tBegin, T const& tEnd ) {
return boost::iterator_range< boost::counting_iterator<T> >( tBegin, tEnd );
}
template< class Func >
class compare_less_impl {
private:
Func m_func;
public:
typedef bool result_type;
compare_less_impl( Func func )
: m_func( func )
{}
template< class T1, class T2 > bool operator()( T1 const& tLeft, T2 const& tRight ) const {
return m_func( tLeft ) < m_func( tRight );
}
};
template< class Func >
compare_less_impl<Func> compare_less( Func func ) {
return compare_less_impl<Func>( func );
}
/////////////////////////////////////////////////////////////////////////////////////////////
// stable_unique
template<class forward_iterator, class predicate_type>
forward_iterator stable_unique(forward_iterator itBegin, forward_iterator itEnd, predicate_type predLess) {
typedef std::iterator_traits<forward_iterator>::difference_type index_type;
struct SIteratorIndex {
SIteratorIndex(forward_iterator itValue, index_type idx) : m_itValue(itValue), m_idx(idx) {}
std::iterator_traits<forward_iterator>::reference Value() const {return *m_itValue;}
index_type m_idx;
private:
forward_iterator m_itValue;
};
// {1} create array of values (represented by iterators) and indices
std::vector<SIteratorIndex> vecitidx;
vecitidx.reserve( std::distance(itBegin, itEnd) );
struct FPushBackIteratorIndex {
FPushBackIteratorIndex(std::vector<SIteratorIndex>& vecitidx) : m_vecitidx(vecitidx) {}
void operator()(forward_iterator itValue) const {
m_vecitidx.push_back( SIteratorIndex(itValue, m_vecitidx.size()) );
}
private:
std::vector<SIteratorIndex>& m_vecitidx;
};
for_each( make_counting_range(itBegin, itEnd), FPushBackIteratorIndex(vecitidx) );
// {2} sort by underlying value
struct FStableCompareByValue {
FStableCompareByValue(predicate_type predLess) : m_predLess(predLess) {}
bool operator()(SIteratorIndex const& itidxA, SIteratorIndex const& itidxB) {
return m_predLess(itidxA.Value(), itidxB.Value())
// stable sort order, index is secondary criterion
|| !m_predLess(itidxB.Value(), itidxA.Value()) && itidxA.m_idx < itidxB.m_idx;
}
private:
predicate_type m_predLess;
};
sort( vecitidx, FStableCompareByValue(predLess) );
// {3} apply std::unique to the sorted vector, removing duplicate values
vecitidx.erase(
std::unique( vecitidx.begin(), vecitidx.end(),
!boost::bind( predLess,
// redundand boost::mem_fn required to compile
boost::bind(boost::mem_fn(&SIteratorIndex::Value), _1),
boost::bind(boost::mem_fn(&SIteratorIndex::Value), _2)
)
),
vecitidx.end()
);
// {4} re-sort by index to match original order
sort( vecitidx, compare_less(boost::mem_fn(&SIteratorIndex::m_idx)) );
// {5} keep only those values in the original range that were not removed by std::unique
std::vector<SIteratorIndex>::iterator ititidx = vecitidx.begin();
forward_iterator itSrc = itBegin;
index_type idx = 0;
for(;;) {
if( ititidx==vecitidx.end() ) {
// {6} return end of unique range
return itSrc;
}
if( idx!=ititidx->m_idx ) {
// original range must be modified
break;
}
++ititidx;
++idx;
++itSrc;
}
forward_iterator itDst = itSrc;
do {
++idx;
++itSrc;
// while there are still items in vecitidx, there must also be corresponding items in the original range
if( idx==ititidx->m_idx ) {
std::swap( *itDst, *itSrc ); // C++0x move
++ititidx;
++itDst;
}
} while( ititidx!=vecitidx.end() );
// {6} return end of unique range
return itDst;
}
template<class forward_iterator>
forward_iterator stable_unique(forward_iterator itBegin, forward_iterator itEnd) {
return stable_unique( itBegin, itEnd, std::less< std::iterator_traits<forward_iterator>::value_type >() );
}
void stable_unique_test() {
std::vector<int> vecn;
vecn.push_back(1);
vecn.push_back(17);
vecn.push_back(-100);
vecn.push_back(17);
vecn.push_back(1);
vecn.push_back(17);
vecn.push_back(53);
vecn.erase( stable_unique(vecn.begin(), vecn.end()), vecn.end() );
// result: 1, 17, -100, 53
}
答案 8 :(得分:1)
据我所知,stl中没有。查看reference。
答案 9 :(得分:1)
基于@ Corden的答案,但使用lambda表达式并删除重复项而不是在输出向量中写入它们
set<int> s;
vector<int> nodups;
remove_copy_if(v.begin(), v.end(), back_inserter(nodups),
[&s](int x){
return !s.insert(x).second; //-> .second false means already exists
} );
答案 10 :(得分:0)