Inplace union排序的向量

时间:2010-09-03 04:45:35

标签: c++ algorithm stl vector stdvector

我想要一种有效的方法,用于对已排序的向量与另一个有序向量进行 inplace 联合。通过inplace,我的意思是算法不应该创建一个全新的向量或其他存储来存储联合,即使是暂时的。相反,第一个向量应该简单地增加新元素的数量。

类似的东西:

void inplace_union(vector & A, const vector & B);

之后, A 包含 A 联合 B 的所有元素,并且已排序。< / p> std::set_union中的

<algorithm>无法正常工作,因为它会覆盖其目的地,即 A

另外,这可以通过两个向量一次传递来完成吗?

修改:两者中的元素 A B只应在A中出现一次。

3 个答案:

答案 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,