マージソートアルゴリズムを使用してインプレースでソートする方法は?


244

質問は具体的すぎません。

私が欲しいのは、通常のマージソートをインプレースマージソート(または一定の余分なスペースオーバーヘッドがあるマージソート)に変換する方法を教えてくれる人です。

(ネット上で)見つけることができるのは、「複雑すぎる」または「このテキストの範囲外」と言っているページだけです。

(余分なスペースなしで)インプレースでマージする唯一の既知の方法は複雑すぎて、実用的なプログラムに削減できません。(ここから取得)

複雑すぎても、マージソートをインプレースにする方法の基本的な概念は何ですか?


:昨日からの質問を読ん際ニース質問、私は自分自身ことを尋ねたstackoverflow.com/questions/2566459/...
クリスLercher

ここで説明するかなり単純な方法があります:xinok.wordpress.com/2014/08/17/...
ブランコDimitrijevic

回答:


140

Knuthはこれを演習(Vol 3、5.2.5)として残しました。インプレースマージソートが存在します。それらは注意深く実装する必要があります。

まず、ここで説明するような単純なインプレースマージは適切なソリューションはありません。パフォーマンスがO(N 2に低下します。

アイデアは、マージの作業領域として残りを使用しながら、配列の一部をソートすることです。

たとえば、次のマージ関数のように。

void wmerge(Key* xs, int i, int m, int j, int n, int w) {
    while (i < m && j < n)
        swap(xs, w++, xs[i] < xs[j] ? i++ : j++);
    while (i < m)
        swap(xs, w++, i++);
    while (j < n)
        swap(xs, w++, j++);
}  

これは、アレイを取るxs2つのソートサブアレイは範囲として表され、[i, m)そして[j, n)それぞれ。作業領域はから始まりwます。ほとんどの教科書で提供されている標準のマージアルゴリズムと比較すると、これはソートされたサブ配列と作業領域の間でコンテンツを交換します。その結果、以前の作業領域にはマージされたソート済み要素が含まれ、作業領域に格納されている以前の要素は2つのサブ配列に移動されます。

ただし、満たさなければならない2つの制約があります。

  1. 作業領域は、配列の境界内にある必要があります。つまり、範囲外のエラーを発生させることなく、交換された要素を保持できる大きさである必要があります。
  2. 作業領域は、並べ替えられた2つの配列のいずれかとオーバーラップできます。ただし、マージされていない要素が上書きされないようにする必要があります。

このマージアルゴリズムを定義すると、配列の半分を並べ替えることができるソリューションを簡単に想像できます。次の質問は、以下に示すように、作業領域に格納されているソートされていない残りの部分を処理する方法です。

... unsorted 1/2 array ... | ... sorted 1/2 array ...

直感的なアイデアの1つは、作業領域のもう半分を再帰的にソートすることです。したがって、まだソートされていない要素は1/4のみです。

... unsorted 1/4 array ... | sorted 1/4 array B | sorted 1/2 array A ...

この段階で重要なのは、ソートされた1/4エレメントBとソートされた1/2エレメントAを遅かれ早かれマージする必要があることです。

AとBを結合するのに十分な大きさの、1/4要素のみを保持する作業領域が残っていますか?残念ながらそうではありません。

ただし、上記の2番目の制約からヒントが得られます。マージされていない要素が上書きされないようにマージシーケンスを確保できる場合は、作業領域をいずれかのサブ配列と重複するように配置することで、それを活用できます。

実際には、作業領域の後半をソートする代わりに、前半をソートして、次のように2つのソートされた配列の間に作業領域を置くことができます。

... sorted 1/4 array B | unsorted work area | ... sorted 1/2 array A ...

この設定では、作業領域がサブアレイAと重なるように効果的に配置されます。このアイデアは、[Jyrki Katajainen、Tomi Pasanen、Jukka Teuholaで提案されています。``実用的なインプレースマージソート ''。Nordic Journal of Computing、1996]。

したがって、残っている唯一のことは、上記の手順を繰り返すことです。これにより、作業領域が1 / 2、1 / 4、1 / 8、...から減少します。作業領域が十分に小さくなると(たとえば、要素が2つだけ残る)、このアルゴリズムを終了するには、簡単な挿入ソートに切り替えます。

これは、このペーパーに基づくANSI Cでの実装です。

void imsort(Key* xs, int l, int u);

void swap(Key* xs, int i, int j) {
    Key tmp = xs[i]; xs[i] = xs[j]; xs[j] = tmp;
}

/* 
 * sort xs[l, u), and put result to working area w. 
 * constraint, len(w) == u - l
 */
void wsort(Key* xs, int l, int u, int w) {
    int m;
    if (u - l > 1) {
        m = l + (u - l) / 2;
        imsort(xs, l, m);
        imsort(xs, m, u);
        wmerge(xs, l, m, m, u, w);
    }
    else
        while (l < u)
            swap(xs, l++, w++);
}

void imsort(Key* xs, int l, int u) {
    int m, n, w;
    if (u - l > 1) {
        m = l + (u - l) / 2;
        w = l + u - m;
        wsort(xs, l, m, w); /* the last half contains sorted elements */
        while (w - l > 2) {
            n = w;
            w = l + (n - l + 1) / 2;
            wsort(xs, w, n, l);  /* the first half of the previous working area contains sorted elements */
            wmerge(xs, l, l + n - w, n, u, w);
        }
        for (n = w; n > l; --n) /*switch to insertion sort*/
            for (m = n; m < u && xs[m] < xs[m-1]; ++m)
                swap(xs, m, m - 1);
    }
}

wmergeは以前に定義されています。

完全なソースコードはここにあり、詳細な説明はここにあります

ちなみに、このバージョンはより多くのスワップ操作を必要とするため、最高速のマージソートではありません。私のテストによると、再帰ごとに余分なスペースを割り当てる標準バージョンよりも高速です。ただし、最適化されたバージョンよりも遅く、元の配列を事前に2倍にし、それをさらにマージするために使用します。


6
Knuth left this as an exercise (Vol 3, 5.2.5).exを参照してください。13. [40] [このセクションの終わりに]提案されている内部ソート方法を実装し、ランダムデータを O(N)単位の時間でソートし、 O(sqrt(N))の追加のメモリ位置のみを生成します。?(40 は、教室の状況での長期プロジェクトとしておそらく適している、かなり困難または長い問題を示しています。
greybeard

4
penguin.ewサイトで言及されているインプレースアルゴリズムの時間の複雑さはO(log n * n ^ 2)だと思います。lognマージがあり、各マージはO(n ^ 2)のオーダーであるためです。そうじゃないの?
code4fun 2016

1
このアルゴリズムはまだ安定していて、n log n時間ですか?
Paul Stelian、

1
@PaulStelian-安定していません。作業領域の要素は、並べ替えられた領域の要素に対する順序付け操作に従って再配置されます。つまり、等しい値を持つ作業領域要素は再配置されるため、元の順序ではなくなります。
rcgldr

1
@PaulStelian-Wikiには、ブロックマージソートに関する記事があり、コメントどおり安定しています。少なくとも2・sqrt(n)の一意の値がある場合に最適に機能します。これにより、配列の作業領域を提供し、安定した状態を保つためにそれらを並べ替えることができます。
rcgldr

59

この「大きな結果」を含め、このホワイトペーパーでは、インプレースマージソート(PDF)のいくつかの変形について説明します。

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf

少ない移動でインプレースソート

ジルキ・カタジャイネン、トミ・A・パサネン

n要素の配列は、O(1)余分なスペース、O(n log n / log log n)要素の移動、およびn log 2 n + O(n log log n)の比較を使用してソートできることが示されています。これは、O(n log n)の比較を保証しながら最悪の場合にo(n log n)の移動を必要とする最初のインプレースソートアルゴリズムですが、一定の要因が関与するため、アルゴリズムは主に理論的に重要です。

これも関係があると思います。私はそれの周りに横になっていて、同僚から受け渡されたプリントアウトを持っていますが、私はそれを読んでいません。それは基本的な理論をカバーしているようですが、私はどれほど包括的に判断するかについて十分に詳しくありません。

http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681

最適な安定したマージ

アントニオス・シンボニス

このペーパーでは、サイズmとn、m≤nの2つのシーケンスAとBをそれぞれO(m + n)割り当て、O(mlog(n / m + 1))比較、および定数のみを使用して安定してマージする方法を示します追加スペースの量。この結果はすべての既知の下限に一致します...


12

これは本当に簡単でも効率的でもないので、本当に必要な場合を除いて、実行しないことをお勧めします(また、インプレースマージのアプリケーションはほとんど理論的なため、これが宿題でない限り、おそらく必要はありません)。代わりにクイックソートを使用できませんか?クイックソートは、いくつかの単純な最適化によりとにかく高速になり、追加のメモリはO(log N)です。

とにかく、あなたがそれをしなければならないなら、あなたはしなければなりません。これが私が見つけたものです:onetwo。インプレースマージソートについてはよく知りませんが、基本的な考え方は、ローテーションを使用して、余分なメモリを使用せずに2つの配列をマージしやすくすることです。

これは、定位置にない従来のマージソートよりも遅いことに注意してください。


9
クイックソートは安定していません。それ多くの量産コードにとって本当に重要です。
ドナルフェロー

7
クイックソートは安定している可能性があり、iircマージソートは、配置されている場合は必ずしも安定しているとは限りません
jk。

4
@jk:クイックソートは安定していません。それは速度から派生するものであり、そうでなければ主張しないでください。これは非常に良いトレードオフです。はい、元のインデックスを残りのキーに関連付けることができるため、2つの要素が同じになることはなく、安定した並べ替えが可能になります。これは、パフォーマンスを破壊する余分な要素の動きに頼らずに「同等の」要素の相対的な順序を維持できないため、余分なスペース(要素の数に比例)の必要なコストがかかります。
ドナルフェロー


4
@DonalFellows:jkはまさに正しいです。クイックソートは、余分なスペースを費やすことなく安定して実装できます。ウィキペディアを確認してください。
Rusty

10

重要なステップは、マージ自体をインプレースにすることです。これらの情報源が理解するほど難しくはありませんが、試行すると何かが失われます。

マージの1つのステップを見る:

[...リスト- 並べ替え ... | x ...リスト-A ... | y ...リスト-B ...]

我々はことを知っているソート順序は、その少ない他のすべてよりもされ、xは以下で他のすべてよりもA、およびというyは以下で他のすべてよりもBxy以下の場合は、ポインタをAの先頭に移動します。場合にはyは未満であるX、あなたがシャッフルするんだyとの全体を過ぎてAソートされました。その最後のステップは、これを高価にするものです(退化した場合を除いて)。

ある程度のスペースと時間をトレードオフし、別の一時配列を交互に並べ替える方が、一般的に安価です(特に、配列が実際に要素ごとに単一の単語のみを含む場合(たとえば、文字列または構造体へのポインター)。


5
インプレースマージはO(m n)の最悪の場合の複雑さを持ちます。ここで、mはAサイズ、nはBサイズです。これは、Aの最初の項目がBの最後の項目よりも大きい場合です。複雑さはO(k log(k)+ m + n)に改善できます。ここで、k = min(m、n) AとBの間のヒープ。このヒープには、Aの項目が含まれている必要があります。Aの項目は、Bの残りの項目より大きく、Aの残りの項目よりも小さい必要があります。それ以外の場合は、ヒープをAの先頭に移動する必要があります。次に、ヒープ項目をインプレースでポップアウトし、反転してマージを完了する必要があります。
valyala

2
@valyalaヒープを使用すると、ソートが安定しなくなることに注意してください。また、ヒープを使用する場合は、マージソートの代わりにヒープソートを使用できます。
martinkunev 14


4

Cのバッファーレスマージソートの例

#define SWAP(type, a, b) \
    do { type t=(a);(a)=(b);(b)=t; } while (0)

static void reverse_(int* a, int* b)
{
    for ( --b; a < b; a++, b-- )
       SWAP(int, *a, *b);
}
static int* rotate_(int* a, int* b, int* c)
/* swap the sequence [a,b) with [b,c). */
{
    if (a != b && b != c)
     {
       reverse_(a, b);
       reverse_(b, c);
       reverse_(a, c);
     }
    return a + (c - b);
}

static int* lower_bound_(int* a, int* b, const int key)
/* find first element not less than @p key in sorted sequence or end of
 * sequence (@p b) if not found. */
{
    int i;
    for ( i = b-a; i != 0; i /= 2 )
     {
       int* mid = a + i/2;
       if (*mid < key)
          a = mid + 1, i--;
     }
    return a;
}
static int* upper_bound_(int* a, int* b, const int key)
/* find first element greater than @p key in sorted sequence or end of
 * sequence (@p b) if not found. */
{
    int i;
    for ( i = b-a; i != 0; i /= 2 )
     {
       int* mid = a + i/2;
       if (*mid <= key)
          a = mid + 1, i--;
     }
    return a;
}

static void ip_merge_(int* a, int* b, int* c)
/* inplace merge. */
{
    int n1 = b - a;
    int n2 = c - b;

    if (n1 == 0 || n2 == 0)
       return;
    if (n1 == 1 && n2 == 1)
     {
       if (*b < *a)
          SWAP(int, *a, *b);
     }
    else
     {
       int* p, * q;

       if (n1 <= n2)
          p = upper_bound_(a, b, *(q = b+n2/2));
       else
          q = lower_bound_(b, c, *(p = a+n1/2));
       b = rotate_(p, b, q);

       ip_merge_(a, p, b);
       ip_merge_(b, q, c);
     }
}

void mergesort(int* v, int n)
{
    if (n > 1)
     {
       int h = n/2;
       mergesort(v, h); mergesort(v+h, n-h);
       ip_merge_(v, v+h, v+n);
     }
}

アダプティブマージソートの例(最適化)。

任意のサイズの補助バッファーが利用可能な場合(追加のメモリーがなくても機能します)、サポートコードと変更を追加してマージを加速します。前方および後方マージ、リングローテーション、小さなシーケンスのマージとソート、および反復マージソートを使用します。

#include <stdlib.h>
#include <string.h>

static int* copy_(const int* a, const int* b, int* out)
{
    int count = b - a;
    if (a != out)
       memcpy(out, a, count*sizeof(int));
    return out + count;
}
static int* copy_backward_(const int* a, const int* b, int* out)
{
    int count = b - a;
    if (b != out)
       memmove(out - count, a, count*sizeof(int));
    return out - count;
}

static int* merge_(const int* a1, const int* b1, const int* a2,
  const int* b2, int* out)
{
    while ( a1 != b1 && a2 != b2 )
       *out++ = (*a1 <= *a2) ? *a1++ : *a2++;
    return copy_(a2, b2, copy_(a1, b1, out));
}
static int* merge_backward_(const int* a1, const int* b1,
  const int* a2, const int* b2, int* out)
{
    while ( a1 != b1 && a2 != b2 )
       *--out = (*(b1-1) > *(b2-1)) ? *--b1 : *--b2;
    return copy_backward_(a1, b1, copy_backward_(a2, b2, out));
}

static unsigned int gcd_(unsigned int m, unsigned int n)
{
    while ( n != 0 )
     {
       unsigned int t = m % n;
       m = n;
       n = t;
     }
    return m;
}
static void rotate_inner_(const int length, const int stride,
  int* first, int* last)
{
    int* p, * next = first, x = *first;
    while ( 1 )
     {
       p = next;
       if ((next += stride) >= last)
          next -= length;
       if (next == first)
          break;
       *p = *next;
     }
    *p = x;
}
static int* rotate_(int* a, int* b, int* c)
/* swap the sequence [a,b) with [b,c). */
{
    if (a != b && b != c)
     {
       int n1 = c - a;
       int n2 = b - a;

       int* i = a;
       int* j = a + gcd_(n1, n2);

       for ( ; i != j; i++ )
          rotate_inner_(n1, n2, i, c);
     }
    return a + (c - b);
}

static void ip_merge_small_(int* a, int* b, int* c)
/* inplace merge.
 * @note faster for small sequences. */
{
    while ( a != b && b != c )
       if (*a <= *b)
          a++;
       else
        {
          int* p = b+1;
          while ( p != c && *p < *a )
             p++;
          rotate_(a, b, p);
          b = p;
        }
}
static void ip_merge_(int* a, int* b, int* c, int* t, const int ts)
/* inplace merge.
 * @note works with or without additional memory. */
{
    int n1 = b - a;
    int n2 = c - b;

    if (n1 <= n2 && n1 <= ts)
     {
       merge_(t, copy_(a, b, t), b, c, a);
     }
    else if (n2 <= ts)
     {
       merge_backward_(a, b, t, copy_(b, c, t), c);
     }
    /* merge without buffer. */
    else if (n1 + n2 < 48)
     {
       ip_merge_small_(a, b, c);
     }
    else
     {
       int* p, * q;

       if (n1 <= n2)
          p = upper_bound_(a, b, *(q = b+n2/2));
       else
          q = lower_bound_(b, c, *(p = a+n1/2));
       b = rotate_(p, b, q);

       ip_merge_(a, p, b, t, ts);
       ip_merge_(b, q, c, t, ts);
     }
}
static void ip_merge_chunk_(const int cs, int* a, int* b, int* t,
  const int ts)
{
    int* p = a + cs*2;
    for ( ; p <= b; a = p, p += cs*2 )
       ip_merge_(a, a+cs, p, t, ts);
    if (a+cs < b)
       ip_merge_(a, a+cs, b, t, ts);
}

static void smallsort_(int* a, int* b)
/* insertion sort.
 * @note any stable sort with low setup cost will do. */
{
    int* p, * q;
    for ( p = a+1; p < b; p++ )
     {
       int x = *p;
       for ( q = p; a < q && x < *(q-1); q-- )
          *q = *(q-1);
       *q = x;
     }
}
static void smallsort_chunk_(const int cs, int* a, int* b)
{
    int* p = a + cs;
    for ( ; p <= b; a = p, p += cs )
       smallsort_(a, p);
    smallsort_(a, b);
}

static void mergesort_lower_(int* v, int n, int* t, const int ts)
{
    int cs = 16;
    smallsort_chunk_(cs, v, v+n);
    for ( ; cs < n; cs *= 2 )
       ip_merge_chunk_(cs, v, v+n, t, ts);
}

static void* get_buffer_(int size, int* final)
{
    void* p = NULL;
    while ( size != 0 && (p = malloc(size)) == NULL )
       size /= 2;
    *final = size;
    return p;
}
void mergesort(int* v, int n)
{
    /* @note buffer size may be in the range [0,(n+1)/2]. */
    int request = (n+1)/2 * sizeof(int);
    int actual;
    int* t = (int*) get_buffer_(request, &actual);

    /* @note allocation failure okay. */
    int tsize = actual / sizeof(int);
    mergesort_lower_(v, n, t, tsize);
    free(t);
}

2
これ書いた?他の答えで表されている困難をどのように克服しますか?その実行時間は何ですか?
Thomas Ahle 2014

これは私自身のカスタムライブラリから採用されていますが、それがあなたが求めているものである場合、私はこれらのアルゴリズムを作成しませんでした。成長は補助メモリなしでO(n(log n)^ 2)です。バッファがいっぱいのO(n log n)。これは実用的な実装であり、構成アルゴリズムを示すように構成されています。
ジョニーケージ2014年

2つのソートされたリストをマージするために、再帰または追加のバッファーが必要なのはなぜですか?2つのポインタを前方に移動し、左が右よりも大きい場合は交換することでそれを実現できると思います。
ジャック14

3

この回答にはコード例があり、これはBing-Chao HuangとMichael A. LangstonによるペーパーPractical In-Place Mergingで説明されているアルゴリズムを実装しています。私は詳細を理解していないことを認めなければなりませんが、マージステップの与えられた複雑さはO(n)です。

実用的な観点からは、純粋なインプレース実装が実際のシナリオではうまく機能しないという証拠があります。たとえば、C ++標準ではstd :: inplace_mergeが定義されています。これは、名前がインプレースマージ操作を意味するためです。

C ++ライブラリは通常非常によく最適化されていると仮定すると、それがどのように実装されているかを見るのは興味深いことです。

1)libstdc ++(GCCコードベースの一部):std :: inplace_merge

実装は__inplace_mergeに委任します。これは、一時バッファーを割り当てようとすることで問題を回避します。

typedef _Temporary_buffer<_BidirectionalIterator, _ValueType> _TmpBuf;
_TmpBuf __buf(__first, __len1 + __len2);

if (__buf.begin() == 0)
  std::__merge_without_buffer
    (__first, __middle, __last, __len1, __len2, __comp);
else
  std::__merge_adaptive
   (__first, __middle, __last, __len1, __len2, __buf.begin(),
     _DistanceType(__buf.size()), __comp);

それ以外の場合は、追加のメモリを必要としない実装(__merge_without_buffer)にフォールバックしますが、O(n)時間で実行されなくなります。

2)libc ++(Clangコードベースの一部):std :: inplace_merge

似ています。関数に委譲し、関数バッファ割り当てようとします。十分な要素があるかどうかに応じて、実装を選択します。定数メモリのフォールバック関数は__buffered_inplace_mergeと呼ばれます

フォールバックでさえもO(n)時間である可能性がありますが、重要なのは、一時メモリが利用可能な場合は実装を使用しないことです。


C ++標準では、必要な複雑さをO(n)からO(N log N)に下げることで、このアプローチを選択する自由を明示的に実装に与えていることに注意してください。

複雑さ: 十分な追加メモリが利用可能な場合、正確にN-1の比較。メモリが不足している場合は、O(N log N)比較。

もちろん、これはO(n)時間での一定のインプレースマージが使用されてはならないという証拠とは見なせません。一方、もしそれがより速いなら、最適化されたC ++ライブラリはおそらくそのタイプの実装に切り替わります。


2

これは私のCバージョンです。

void mergesort(int *a, int len) {
  int temp, listsize, xsize;

  for (listsize = 1; listsize <= len; listsize*=2) {
    for (int i = 0, j = listsize; (j+listsize) <= len; i += (listsize*2), j += (listsize*2)) {
      merge(& a[i], listsize, listsize);
    }
  }

  listsize /= 2;

  xsize = len % listsize;
  if (xsize > 1)
    mergesort(& a[len-xsize], xsize);

  merge(a, listsize, xsize);
}

void merge(int *a, int sizei, int sizej) {
  int temp;
  int ii = 0;
  int ji = sizei;
  int flength = sizei+sizej;

  for (int f = 0; f < (flength-1); f++) {
    if (sizei == 0 || sizej == 0)
      break;

    if (a[ii] < a[ji]) {
      ii++;
      sizei--;
    }
    else {
      temp = a[ji];

      for (int z = (ji-1); z >= ii; z--)
        a[z+1] = a[z];  
      ii++;

      a[f] = temp;

      ji++;
      sizej--;
    }
  }
}

この実装には、最悪の場合(逆配列)でΘ(n ^ 2 log n)時間かかることに注意してください。
martinkunev 2014

1

Kronrodの元の手法を使用したインプレースマージソートの実装は比較的単純ですが、実装は単純です。この手法を説明する図の例は、http//www.logiccoder.com/TheSortProblem/BestMergeInfo.htmにあります

このリンクに関連付けられている同じ著者によるより詳細な理論的分析へのリンクもあります。


このリンクの結果、403
シャーロットタン

3
リンクを修正しました。そこにあるドキュメントは、鈍化の点まで不可解です。そこには興味深いアイデアがあるという印象を受けますが、アルゴリズムは提示されておらず、一連の図といくつかのやや弱い説明しかありません。弱い説明を図に結び付けることができなかったので、あきらめました。
Ira Baxter

-6

次の手順を使用して、挿入ソートアルゴリズムを使用して、JAVAでマージソートのインプレイスマージアルゴリズムを試しました。
1)2つの並べ替えられた配列が利用可能です。
2)各配列の最初の値を比較します。最小の値を最初の配列に配置します。
3)挿入ソート(左から右にトラバース)を使用して、大きい方の値を2番目の配列に配置します。
4)次に、最初の配列の2番目の値と2番目の配列の最初の値を比較して、同じことを行います。しかし、スワッピングが発生した場合、それ以上のアイテムの比較をスキップする手掛かりがいくつかありますが、スワッピングだけが必要です。

ここでいくつかの最適化を行いました。挿入ソートでの比較を少なくするため。
私がこのソリューションで見つけた唯一の欠点は、2番目の配列で配列要素のより大きなスワッピングが必要なことです。

例えば)

First___Array:3、7、8、9

2番目の配列:1、2、4、5

次に、7、8、9は2番目の配列を作成して、すべての要素を1つずつ入れ替え(左に1つ移動)、最後の配列に配置します。

したがって、ここでの項目の交換という仮定は、2つの項目の比較と比較して無視できます。

https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java

package sorting;

import java.util.Arrays;

public class MergeSort {
    public static void main(String[] args) {
    int[] array = { 5, 6, 10, 3, 9, 2, 12, 1, 8, 7 };
    mergeSort(array, 0, array.length -1);
    System.out.println(Arrays.toString(array));

    int[] array1 = {4, 7, 2};
    System.out.println(Arrays.toString(array1));
    mergeSort(array1, 0, array1.length -1);
    System.out.println(Arrays.toString(array1));
    System.out.println("\n\n");

    int[] array2 = {4, 7, 9};
    System.out.println(Arrays.toString(array2));
    mergeSort(array2, 0, array2.length -1);
    System.out.println(Arrays.toString(array2));
    System.out.println("\n\n");

    int[] array3 = {4, 7, 5};
    System.out.println(Arrays.toString(array3));
    mergeSort(array3, 0, array3.length -1);
    System.out.println(Arrays.toString(array3));
    System.out.println("\n\n");

    int[] array4 = {7, 4, 2};
    System.out.println(Arrays.toString(array4));
    mergeSort(array4, 0, array4.length -1);
    System.out.println(Arrays.toString(array4));
    System.out.println("\n\n");

    int[] array5 = {7, 4, 9};
    System.out.println(Arrays.toString(array5));
    mergeSort(array5, 0, array5.length -1);
    System.out.println(Arrays.toString(array5));
    System.out.println("\n\n");

    int[] array6 = {7, 4, 5};
    System.out.println(Arrays.toString(array6));
    mergeSort(array6, 0, array6.length -1);
    System.out.println(Arrays.toString(array6));
    System.out.println("\n\n");

    //Handling array of size two
    int[] array7 = {7, 4};
    System.out.println(Arrays.toString(array7));
    mergeSort(array7, 0, array7.length -1);
    System.out.println(Arrays.toString(array7));
    System.out.println("\n\n");

    int input1[] = {1};
    int input2[] = {4,2};
    int input3[] = {6,2,9};
    int input4[] = {6,-1,10,4,11,14,19,12,18};
    System.out.println(Arrays.toString(input1));
    mergeSort(input1, 0, input1.length-1);
    System.out.println(Arrays.toString(input1));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input2));
    mergeSort(input2, 0, input2.length-1);
    System.out.println(Arrays.toString(input2));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input3));
    mergeSort(input3, 0, input3.length-1);
    System.out.println(Arrays.toString(input3));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input4));
    mergeSort(input4, 0, input4.length-1);
    System.out.println(Arrays.toString(input4));
    System.out.println("\n\n");
}

private static void mergeSort(int[] array, int p, int r) {
    //Both below mid finding is fine.
    int mid = (r - p)/2 + p;
    int mid1 = (r + p)/2;
    if(mid != mid1) {
        System.out.println(" Mid is mismatching:" + mid + "/" + mid1+ "  for p:"+p+"  r:"+r);
    }

    if(p < r) {
        mergeSort(array, p, mid);
        mergeSort(array, mid+1, r);
//      merge(array, p, mid, r);
        inPlaceMerge(array, p, mid, r);
        }
    }

//Regular merge
private static void merge(int[] array, int p, int mid, int r) {
    int lengthOfLeftArray = mid - p + 1; // This is important to add +1.
    int lengthOfRightArray = r - mid;

    int[] left = new int[lengthOfLeftArray];
    int[] right = new int[lengthOfRightArray];

    for(int i = p, j = 0; i <= mid; ){
        left[j++] = array[i++];
    }

    for(int i = mid + 1, j = 0; i <= r; ){
        right[j++] = array[i++];
    }

    int i = 0, j = 0;
    for(; i < left.length && j < right.length; ) {
        if(left[i] < right[j]){
            array[p++] = left[i++];
        } else {
            array[p++] = right[j++];
        }
    }
    while(j < right.length){
        array[p++] = right[j++];
    } 
    while(i < left.length){
        array[p++] = left[i++];
    }
}

//InPlaceMerge no extra array
private static void inPlaceMerge(int[] array, int p, int mid, int r) {
    int secondArrayStart = mid+1;
    int prevPlaced = mid+1;
    int q = mid+1;
    while(p < mid+1 && q <= r){
        boolean swapped = false;
        if(array[p] > array[q]) {
            swap(array, p, q);
            swapped = true;
        }   
        if(q != secondArrayStart && array[p] > array[secondArrayStart]) {
            swap(array, p, secondArrayStart);
            swapped = true;
        }
        //Check swapped value is in right place of second sorted array
        if(swapped && secondArrayStart+1 <= r && array[secondArrayStart+1] < array[secondArrayStart]) {
            prevPlaced = placeInOrder(array, secondArrayStart, prevPlaced);
        }
        p++;
        if(q < r) {     //q+1 <= r) {
            q++;
        }
    }
}

private static int placeInOrder(int[] array, int secondArrayStart, int prevPlaced) {
    int i = secondArrayStart;
    for(; i < array.length; i++) {
        //Simply swap till the prevPlaced position
        if(secondArrayStart < prevPlaced) {
            swap(array, secondArrayStart, secondArrayStart+1);
            secondArrayStart++;
            continue;
        }
        if(array[i] < array[secondArrayStart]) {
            swap(array, i, secondArrayStart);
            secondArrayStart++;
        } else if(i != secondArrayStart && array[i] > array[secondArrayStart]){
            break;
        }
    }
    return secondArrayStart;
}

private static void swap(int[] array, int m, int n){
    int temp = array[m];
    array[m] = array[n];
    array[n] = temp;
}
}

3
O(n ^ 2)であり、非常に読みにくい(構文エラーが時々あり、一貫性のない/スタイルが悪いため)
glaba
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.