誰かが簡単な英語で説明してもらえますか、それを簡単に説明できますか?
「従来の」マージソートでは、データを通過するたびに、ソートされたサブセクションのサイズが2倍になります。最初のパスの後、ファイルは長さ2のセクションにソートされます。 2回目のパスの後、長さ4。次に8、16など、ファイルのサイズまで。
ファイル全体を構成するセクションが1つになるまで、並べ替えられたセクションのサイズを2倍にしておく必要があります。ファイルサイズに達するには、セクションサイズをlg(N)倍にする必要があり、データの各パスには、レコード数に比例した時間がかかります。
Merge Sortは、Divide-and-Conquerアプローチを使用して、ソートの問題を解決します。まず、再帰を使用して入力を半分に分割します。分割後、半分をソートし、ソートされた1つの出力にマージします。図をご覧ください
最初に問題の半分をソートし、単純なマージサブルーチンを実行することをお勧めします。そのため、マージサブルーチンの複雑さと、再帰で呼び出される回数を知ることが重要です。
merge sortの擬似コードは本当に簡単です。
# C = output [length = N]
# A 1st sorted half [N/2]
# B 2nd sorted half [N/2]
i = j = 1
for k = 1 to n
if A[i] < B[j]
C[k] = A[i]
i++
else
C[k] = B[j]
j++
すべてのループで、4つの操作があることが簡単にわかります:k ++、i ++または j ++、ifステートメント、および属性C = A | B。そのため、4N + 2以下の操作でO(N)の複雑さが得られます。証明のために、N = 1(4N +2 <= 6N)がtrueであるため、4N + 2は6Nとして扱われます。
したがって、[〜#〜] n [〜#〜]要素を持つ入力があり、[〜#〜] n [〜#〜 ]は2の累乗です。すべてのレベルで、前の入力の半分の要素を持つ入力で2倍以上の副問題があります。これは、レベルj= 0、1、2、...、lgNでは2 ^ j長さN/2 ^ jの入力を持つ副問題。各レベルでの操作の数jは以下になります
2 ^ j * 6(N/2 ^ j)= 6N
常に6N以下の操作を行うレベルは重要ではないことに注意してください。
LgN + 1レベルがあるため、複雑さは
O(6N * (lgN + 1)) = O(6N*lgN + 6N) = O(n lgN)
参照:
これは、マージソートが最悪の場合でも平均的な場合でも、各ステージで配列を2つの半分に分割するだけで、lg(n)コンポーネントと他のNコンポーネントが各ステージで行われる比較から得られるためです。したがって、それを組み合わせるとほぼO(nlg n)になります。平均ケースか最悪ケースかに関係なく、lg(n)係数は常に存在します。残りのN係数は、両方のケースで行われた比較から得られる比較に依存します。最悪のケースは、各ステージでN個の入力に対してN回の比較が行われるケースです。したがって、O(nlg n)になります。
単一の要素があるステージに配列を分割した後、つまりサブリストと呼びます。
各段階で、各サブリストの要素をその隣接するサブリストと比較します。たとえば、[@ Daviの画像の再利用]
log(n) base 2
になるため、合計の複雑さは==(各ステージでの最大比較数*ステージ数)= = O((n/2)* log(n))==> O(nlog(n))再帰ツリーには深さlog(N)
があり、そのツリーの各レベルで、結合されたN
作業を実行して、2つのソートされた配列をマージします。
ソートされた配列のマージ
並べ替えられた2つの配列_A[1,5]
_と_B[3,4]
_をマージするには、両方を最初から繰り返し、2つの配列の中で最も低い要素を選択し、その配列のポインターをインクリメントします。両方のポインターがそれぞれの配列の最後に到達したら、完了です。
_[1,5] [3,4] --> []
^ ^
[1,5] [3,4] --> [1]
^ ^
[1,5] [3,4] --> [1,3]
^ ^
[1,5] [3,4] --> [1,3,4]
^ x
[1,5] [3,4] --> [1,3,4,5]
x x
Runtime = O(A + B)
_
ソート図のマージ
再帰呼び出しスタックは次のようになります。作業は最下位のリーフノードから始まり、バブルアップします。
_beginning with [1,5,3,4], N = 4, depth k = log(4) = 2
[1,5] [3,4] depth = k-1 (2^1 nodes) * (N/2^1 values to merge per node) == N
[1] [5] [3] [4] depth = k (2^2 nodes) * (N/2^2 values to merge per node) == N
_
したがって、ツリーのN
レベルのそれぞれでk
workを実行します。ここで、k = log(N)
N * k = N * log(N)
他の答えの多くは素晴らしいですが、heightおよびdepth「マージソートツリー」の例に関連しています。ここでは、ツリーに重点を置いて質問にアプローチする別の方法を示します。説明に役立つ別の画像を次に示します。
要約:他の回答が指摘したように、シーケンスの2つの並べ替えられたスライスをマージする作業は線形時間で実行されます(メインの並べ替え関数から呼び出すマージヘルパー関数)。
このツリーを見てみましょう。ここでは、ルートの各子孫(ルート以外)をソート関数の再帰呼び出しと考えることができます。各ノードにどれだけの時間を費やしているかを評価してみましょう。 。シーケンスのスライスとマージ(両方)は線形時間を要するため、ノードの実行時間は、そのノードでのシーケンスの長さに対して線形です。
ここでツリーの深さが入ります。nが元のシーケンスの合計サイズである場合、任意のノードでのシーケンスのサイズはn/2です私、ここでiは深さです。これは上の画像に示されています。これを各スライスの線形作業量と合わせると、実行時間はO(n/2私)ツリー内のすべてのノードに対して。ここで、n個のノードについて合計する必要があります。これを行う1つの方法は、2私 ツリーの各深さレベルのノード。したがって、どのレベルでも、O(2私 * n/2私)、これはO(n)です。これは2私s!各深さがO(n)である場合、lognであるこのバイナリツリーのheightを掛けるだけです。回答:O(nlogn)
MergeSortアルゴリズムには3つのステップがあります。
このアルゴリズムは、n個の要素の配列をソートするために約lognパスを必要とするため、総時間の複雑さはnlognです。