次のクイックソートパーティショニングアルゴリズムは、安定したソートをもたらしますか(つまり、等しい値を持つ要素の相対位置を維持しますか):
partition(A,p,r)
{
x=A[r];
i=p-1;
for j=p to r-1
if(A[j]<=x)
i++;
exchange(A[i],A[j])
exchang(A[i+1],A[r]);
return i+1;
}
パーティショニングアルゴリズムがスワップを作成して、等しい値の順序を変更する場合が1つあります。これは、インプレースパーティショニングアルゴリズムがどのように機能するかを示すのに役立つ画像です。
Jインデックスを使用して各値を行進し、表示された値がパーティション値よりも小さい場合は、ライトグレーサブアレイのすぐ右側にある要素と交換してライトグレーサブアレイに追加します。 。ライトグレーのサブアレイには、パーティション値<=であるすべての要素が含まれています。ここで、たとえばステージ(c)を見て、3つの9がホワイトゾーンの先頭にあり、その後に1が続く場合を考えてみましょう。つまり、9が<=パーティション値であるかどうかを確認しようとしています。 。最初の9を見ると、<= 4ではないことがわかります。そのため、そのままにして、jを前に進めます。次の9を見ると、<= 4ではないことがわかります。そのため、そのままにして、jを前に進めます。また、3番目の9もそのままにしておきます。ここで1を見て、それがパーティションよりも小さいことを確認したので、最初の9と交換します。次に、アルゴリズムを終了するために、パーティション値をi + 1の値(2番目の9)と交換します。これでパーティションアルゴリズムが完了し、元々3番目だった9が最初になりました。
2番目のキーを追加する場合は、どのソートも安定ソートに変換できます。 2番目のキーは、シーケンス番号など、元の順序を示すものである必要があります。比較関数で、最初のキーが等しい場合は、2番目のキーを使用します。
類似要素の元の順序が変更されない場合、並べ替えは安定しています。等しい要素を交換するため、アルゴリズムは安定していません。
そうでない場合でも、安定していません。
( 1, 5, 2, 5, 3 )
ソートキー「5」の要素が2つあります。何らかの理由で要素#2(5)と#5(3)を比較すると、5は3と交換され、安定ソートの契約に違反します。つまり、ピボット要素を慎重に選択しても効果はありません。また、パーティション間で要素をコピーしても元の順序が入れ替わらないようにする必要があります。
あなたのコードは、 wikipedia で与えられたサンプルのパーティション関数に疑わしいほど似ていますが、これは安定していないので、関数はおそらく安定していません。少なくとも、ピボットポイントrがA [r]に等しい値の配列の最後の位置を指していることを確認する必要があります。
クイックソートを安定させることはできますが(マシュージョーンズには同意しません)、デフォルトの最速(heh)形式ではできません。
Martin(コメントを参照)は、最初の要素をピボットとして開始し、append値を最後に追加するリンクリストのクイックソートが正しいことです。配列を調べながら、下位サブリストと上位サブリスト。ただし、クイックソートは、リンクリストではなく単純な配列で機能することになっています。クイックソートの利点の1つは、メモリフットプリントが小さいことです(すべてが適切に行われるため)。リンクリストを使用している場合は、次の値などへのすべてのポインタに対してすでにメモリオーバーヘッドが発生しており、値ではなくそれらを交換しています。
安定したO(n * log(n))ソートが必要な場合は、 mergesort を使用してください。 (ちなみに、クイックソートを安定させる最良の方法は、ピボットとしてランダム値の中央値を選択することです。ただし、これは、同等のすべての要素に対して安定しているわけではありません。)
ウィキペディアから:
クイックソートは比較ソートであり、効率的な実装では安定したソートではありません。
クイックソートは安定していません。これは、安定していない場合です。
5 5 4 8
1番目の5をピボットとして、1番目のパスの後にフォローします-
4 5 5 8
ご覧のとおり、5の順序が変更されています。ソートを続行すると、ソートされた配列の5の順序が変更されます。
この問題を解決する1つの方法は、配列の最後の要素をキーとして使用しないことです。クイックソートはランダム化されたアルゴリズムです。
そのパフォーマンスは、キーの選択に大きく依存します。アルゴリズムdefは、最後または最初の要素をキーとして使用する必要があると言っていますが、実際には、任意の要素をキーとして選択できます。
そこで、配列の最初、中間、最後の要素を取るという3の中央値アプローチを試しました。それらを並べ替えてから、中央の位置をキーとして使用します。
たとえば、私の配列は{9,6,3,10,15}
です。したがって、最初、中間、最後の要素を並べ替えると、{3,6,9,10,15}
になります。ここで、キーとして9を使用します。したがって、キーを最後に移動すると、{3,6,15,10,9}
になります。
私たちが注意する必要があるのは、9が複数回来るとどうなるかだけです。それはそれ自体が複数回来る鍵です。
このような場合、中間インデックスとしてキーを選択した後、キーから右端までの要素を調べる必要があります。同じキーが見つかった場合、つまり、中間位置から最後までの間に9が見つかった場合は、その9をキーにします。
ここで、9より大きい要素の領域、つまりjのループで、9が見つかった場合は、それよりも小さい要素の領域と交換します。これはiの領域です。配列は安定してソートされます。