Menu
Coddy logo textTech

Merge Sort

最終更新

マージソートは分割統治アルゴリズムです。各部分の要素が1つになる(それは自明にソート済み)まで配列を再帰的に半分に分割し、その後、部分を順序を保ちながら併合していきます。マージのステップでは2つのソート済み部分配列を2つのポインタでたどり、常に先頭のより小さい要素を次にコピーします。上の再生を押して、配列がマージごとに組み立て直される様子を見てみましょう。

常に半分に分割するため、マージソートはあらゆる場合で O(n log n) 時間で動作します。最悪の場合でも最良の場合と同じ良さです。その代償として、一時的なマージ用バッファに O(n) の追加空間を使います。

時間計算量と空間計算量

ケース計算量備考
最良の場合O(n log n)常に入力を半分にする
平均の場合O(n log n)ランダムな順序
最悪の場合O(n log n)保証あり - 不利な入力はない
空間O(n)マージ用の一時バッファ
安定はい同値はマージ中に左側を優先して解決

ステップごとの手順

ステップ何が起きるか
1範囲の要素が0個または1個なら、すでにソート済みです。
2範囲を2つの半分に分割します。
3左半分をマージソートで再帰的にソートします。
4右半分をマージソートで再帰的にソートします。
52つのソート済みの半分を2つのポインタでマージします。

具体例

[5, 2, 4, 1] をソートする場合:

パス配列動作
分割[5, 2] | [4, 1]配列を2つの半分に分ける
分割[5] [2] | [4] [1]各部分が単一要素になるまで再度分ける
マージ[2, 5] | [1, 4][5],[2][2, 5] に、[4],[1][1, 4] にマージ
マージ[1, 2, 4, 5][2, 5][1, 4] をマージ:1、次に 2、次に 4、次に 5 を選ぶ
完了[1, 2, 4, 5]配列は完全にソート済み

マージソートを使うべきとき

使うべきとき避けるべきとき
保証された O(n log n) の最悪計算量が必要なときメモリが逼迫し、O(n) の追加空間が許容できないとき
安定性が重要なとき(同じキーが順序を保つ)挿入ソートの方が速い小さな配列をソートするとき
連結リストをソートするときその場ソートが厳しい要件のとき
データがRAMに収まらないほど大きいとき(外部ソート)キャッシュ局所性が支配的で、quicksort のその場のパスが有利なとき

Merge Sortのコード

Python, JavaScript, Java, C++, Cによるクリーンで実行可能なMerge Sortの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのMerge Sortのコード

Python
1def merge_sort(a):2    if len(a) <= 1:3        return a4    mid = len(a) // 25    left = merge_sort(a[:mid])6    right = merge_sort(a[mid:])7    return merge(left, right)8
9
10def merge(left, right):11    out = []12    i = j = 013    while i < len(left) and j < len(right):14        if left[i] <= right[j]:15            out.append(left[i])16            i += 117        else:18            out.append(right[j])19            j += 120    out.extend(left[i:])21    out.extend(right[j:])22    return out23
24
25nums = [38, 27, 43, 3, 9, 82, 10]26print("Before:", nums)27print("After: ", merge_sort(nums))
このコードをPythonプレイグラウンドで実行

マージソートに関するよくある質問

マージソートの時間計算量は?
マージソートは常に配列を半分に分割するため、最良・平均・最悪のいずれの場合も O(n log n) です。マージ用バッファに O(n) の追加空間を使います。
マージソートは安定ですか?
はい、マージのステップが同値を左半分から先に取ることで解決する場合は安定です。これにより同じ要素は元の相対順序を保つため、マージソートは安定ソートの一般的な選択肢になっています。
なぜ quicksort ではなくマージソートを使うのですか?
マージソートは敵対的な入力でも O(n log n) を保証し安定ですが、quicksort は O(n²) に劣化することがあります。マージソートは連結リストや外部ソートでも好まれます。欠点は O(n) の追加メモリです。
マージソートと quicksort の違いは何ですか?
どちらも分割統治のソートですが、quicksort はピボットを軸に分割し O(log n) のスタック空間でその場でソートするのに対し、マージソートは無条件に半分に分けて O(n) のバッファで併合します。quicksort はキャッシュ局所性のため実用上たいてい高速ですが、マージソートは保証された O(n log n) の最悪計算量を持ち安定です。
実際にはいつマージソートを使うべきですか?
保証された O(n log n) 上限を持つ安定ソートが必要なとき、連結リストをソートするとき(ランダムアクセスが不要)、あるいはメモリに収まらない大きなデータを外部ソートするときにマージソートを選びましょう。O(n) の追加空間を要するため、メモリが乏しいときは避けてください。
マージソートはその場でソートしますか?
いいえ。標準的なマージソートは2つの半分を併合するために O(n) の一時バッファを確保するため、その場(in-place)ではありません。その場マージの変種も存在しますが複雑で、遅くなるか安定性を失うため、バッファを用いる版が一般的な選択肢です。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める