3


1

インプレースソートされたベクトル

ソートされたベクトルを別のソートされたベクトルと* inplace *結合するための効率的な方法が欲しいです。 その場で、私は、アルゴリズムが一時的であっても、ユニオンを格納するためのまったく新しいベクトルや他のストレージを作成するべきではないことを意味します。 代わりに、最初のベクトルは新しい要素の数だけ正確に成長するはずです。

何かのようなもの:

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

その後、* A には A * union * B * _and_のすべての要素がソートされます。

``の `std

set_union`は、宛先を上書きするため機能しません。これは* A *になります。

また、これは2つのベクトルの1回のパスで実行できますか?

編集:両方 A および Bにある要素は、Aに1回だけ表示される

3 Answer


6


アルゴリズム `std

inplace_merge`を使用できると思います。 これがサンプルコードです。

void inplace_union(std::vector& a, const std::vector& 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());
}


2


はい。これは、両方の入力がソートされ、両方のベクトルを1回通過することを前提として、インプレースでO(n)時間で実行できます。 方法は次のとおりです。

B.size()`で `A(デスティネーションベクトル)を拡張します-新しい要素のためのスペースを作ります。

「B」の末尾と「A」の元の末尾から開始して、2つのベクトルを逆方向​​に繰り返します。 ベクトルが小さい→大きい(末尾が大きい)場合は、大きい方を指すイテレータを取得し、 `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」を検討しましたか? (私はちょうど再発明したかもしれませんか?)


0


`set_difference`のアイデアは良いのですが、デメリットは事前にベクトルをどれだけ大きくする必要があるかわからないことです。

これは、必要な追加スロットの数をカウントするために1回、実際のコピーを行うためにもう一度、「set_difference」を2回実行する私のソリューションです。

注:つまり、ソースを2回繰り返します。

#include
#include

// for the test
#include
#include


struct output_counter
{
   output_counter(size_t & r) : result(r) {}
   template  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
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 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,