我有一些看起来像这样的代码:
std::set<int> s1, s2, out;
// ... s1 and s2 are populated ...
std::set_intersection(s1.begin(), s1.end(),
s2.begin(), s2.end(),
std::inserter(out, out.end()));
如果插入到集合中的值紧跟在作为“提示”给出的迭代器之后,我读取插入可以在分摊的常量时间内完成。这在运行集合交集时显然是有益的,特别是因为写入out
的所有内容都已按排序顺序排列。
我如何保证这种最佳性能?在创建std::inserter
时,out
为空out.begin() == out.end()
,因此我无法确定是否将out.begin()
或out.end()
指定为提示。但是,如果在begin()
插入每个元素时解释这一点,那么我似乎不会获得最佳的算法性能。这可以做得更好吗?
答案 0 :(得分:5)
我选择亚历山大·盖斯勒作为“正确”答案的答案,因为它让我得到了这个解决方案,我认为无论如何我都会发布。我编写了一个last_inserter()
,它保证插入位置始终是最后一个元素的迭代器(如果为空,则为begin()),因为set
想要一个迭代器到前面的元素 实际插入位置以获得最佳性能(因此不是end() - 也就是实际插入位置之后的那个)。
原始示例的用法如下:
std::set<int> s1, s2, out;
// ... s1 and s2 are populated ...
std::set_intersection(s1.begin(), s1.end(),
s2.begin(), s2.end(),
last_inserter(out)); // note no iterator provided
这保证了插入提示始终是最后一个元素的迭代器,希望在将输出迭代器用于具有排序范围的集合时提供最佳情况性能,如上所述。
以下是我的实施。我认为它是特定于Visual C ++ 2010的STL实现的平台,因为它主要基于现有的insert_iterator
,我只能通过派生std::_Outit
来实现它。如果有人知道如何使这个便携式,请告诉我:
// VC10 STL wants this to be a checked output iterator. I haven't written one, but
// this needs to be defined to silence warnings about this.
#define _SCL_SECURE_NO_WARNINGS
template<class Container>
class last_inserter_iterator : public std::_Outit {
public:
typedef last_inserter_iterator<Container> _Myt;
typedef Container container_type;
typedef typename Container::const_reference const_reference;
typedef typename Container::value_type _Valty;
last_inserter_iterator(Container& cont)
: container(cont)
{
}
_Myt& operator=(const _Valty& _Val)
{
container.insert(get_insert_hint(), _Val);
return (*this);
}
_Myt& operator=(_Valty&& _Val)
{
container.insert(get_insert_hint(), std::forward<_Valty>(_Val));
return (*this);
}
_Myt& operator*()
{
return (*this);
}
_Myt& operator++()
{
return (*this);
}
_Myt& operator++(int)
{
return (*this);
}
protected:
Container& container;
typename Container::iterator get_insert_hint() const
{
// Container is empty: no last element to insert ahead of; just insert at begin.
if (container.empty())
return container.begin();
else
{
// Otherwise return iterator to last element in the container. std::set wants the
// element *preceding* the insert position as a hint, so this should be an iterator
// to the last actual element, not end().
return (--container.end());
}
}
};
template<typename Container>
inline last_inserter_iterator<Container> last_inserter(Container& cont)
{
return last_inserter_iterator<Container>(cont);
}
答案 1 :(得分:2)
您可以使用自定义仿函数代替std::inserter
,并在每次插入新元素时重新调用out.end()
。
或者,如果您的值按降序排序,out.begin()
就可以了。
答案 2 :(得分:1)
根据http://gcc.gnu.org/onlinedocs/gcc-4.8.0/libstdc++/api/a01553_source.html
insert_iterator&
operator=(typename _Container::value_type&& __value)
{
iter = container->insert(iter, std::move(__value));
++iter;
return *this;
}
iter
最初指向您传递给std::inserter
的迭代器。所以它总是指向一个超过你刚刚插入的值,如果你按顺序插入,应该是最佳效率。