我想要一种有效的方法,用于对已排序的向量与另一个有序向量进行 inplace 联合。通过inplace,我的意思是算法不应该创建一个全新的向量或其他存储来存储联合,即使是暂时的。相反,第一个向量应该简单地增加新元素的数量。
类似的东西:
void inplace_union(vector & A, const vector & B);
之后, A 包含 A 联合 B 的所有元素,并且已排序。< / p>
std::set_union
中的
<algorithm>
无法正常工作,因为它会覆盖其目的地,即 A 。
另外,这可以通过两个向量一次传递来完成吗?
修改:两者中的元素 A 和 B只应在A中出现一次。
答案 0 :(得分:6)
我相信你可以使用算法std::inplace_merge
。以下是示例代码:
void inplace_union(std::vector<int>& a, const std::vector<int>& b)
{
int mid = a.size(); //Store the end of first sorted range
//First copy the second sorted range into the destination vector
std::copy(b.begin(), b.end(), std::back_inserter(a));
//Then perform the in place merge on the two sub-sorted ranges.
std::inplace_merge(a.begin(), a.begin() + mid, a.end());
//Remove duplicate elements from the sorted vector
a.erase(std::unique(a.begin(), a.end()), a.end());
}
答案 1 :(得分:2)
是的,这可以就地完成,并且在O( n )时间内,假设两个输入都已排序,并且在两个向量上都有一次传递。方法如下:
A
扩展B.size()
(目的地向量) - 为我们的新元素腾出空间。
从B
的结尾和A
的原始结尾开始,向后迭代这两个向量。如果向量的小→大(最后大),则将迭代器指向较大的数字,并将其粘贴在A
的真端。继续前进,直到B
的迭代器到达B
的开头。反向迭代器应该在这里证明特别好。
示例:
A: [ 1, 2, 4, 9 ]
B: [ 3, 7, 11 ]
* = iterator, ^ = where we're inserting, _ = unitialized
A: [ 1, 3, 4, 9*, _, _, _^ ] B: [ 3, 7, 11* ]
A: [ 1, 3, 4, 9*, _, _^, 11 ] B: [ 3, 7*, 11 ]
A: [ 1, 3, 4*, 9, _^, 9, 11 ] B: [ 3, 7*, 11 ]
A: [ 1, 3, 4*, 9^, 7, 9, 11 ] B: [ 3*, 7, 11 ]
A: [ 1, 3*, 4^, 4, 7, 9, 11 ] B: [ 3*, 7, 11 ]
A: [ 1, 3*^, 3, 4, 7, 9, 11 ] B: [ 3, 7, 11 ]
超级修改:您是否考虑过std::inplace_merge
? (我可能刚刚重新发明了?)
答案 2 :(得分:0)
set_difference
这个想法很好,但缺点是我们不知道我们需要提前多长时间来增加矢量。
这是我的解决方案,set_difference
执行两次,一次计算我们需要的额外插槽数量,再次执行实际复制。
注意:这意味着我们将对源进行两次迭代。
#include <algorithm>
#include <boost/function_output_iterator.hpp>
// for the test
#include <vector>
#include <iostream>
struct output_counter
{
output_counter(size_t & r) : result(r) {}
template <class T> void operator()(const T & x) const { ++result; }
private:
size_t & result;
};
// Target is assumed to work the same as a vector
// Both target and source must be sorted
template <class Target, class It>
void inplace_union( Target & target, It src_begin, It src_end )
{
const size_t mid = target.size(); // Store the end of first sorted range
// first, count how many items we will copy
size_t extra = 0;
std::set_difference(
src_begin, src_end,
target.begin(), target.end(),
boost::make_function_output_iterator(output_counter(extra)));
if (extra > 0) // don't waste time if nothing to do
{
// reserve the exact memory we will require
target.reserve( target.size() + extra );
// Copy the items from the source that are missing in the destination
std::set_difference(
src_begin, src_end,
target.begin(), target.end(),
std::back_inserter(target) );
// Then perform the in place merge on the two sub-sorted ranges.
std::inplace_merge( target.begin(), target.begin() + mid, target.end() );
}
}
int main()
{
std::vector<int> a(3), b(3);
a[0] = 1;
a[1] = 3;
a[2] = 5;
b[0] = 4;
b[1] = 5;
b[2] = 6;
inplace_union(a, b.begin(), b.end());
for (size_t i = 0; i != a.size(); ++i)
std::cout << a[i] << ", ";
std::cout << std::endl;
return 0;
}
使用boost标头编译,结果是:
$ ./test
1, 3, 4, 5, 6,