web-dev-qa-db-ja.com

すべての要素が同じ場合のクイックソートの複雑さ?

同じN個の数値の配列があります。クイックソートを適用しています。この場合、ソートの時間計算量はどうあるべきですか。

私はこの質問をくぐり抜けましたが、正確な説明は得られませんでした。

どんな助けでもいただければ幸いです。

17
Sandeep Pathak

これは、クイックソートの実装によって異なります。 2つの(_<_セクションと_>=_)セクションに分割する従来の実装では、同じ入力にO(n*n)があります。 swapsは必ずしも発生しませんが、nの再帰呼び出しが行われ、それぞれがピボット要素および_n-recursionDepth_要素と比較する必要があります。つまり、O(n*n)の比較を行う必要があります

ただし、3つのセット(_<_、_=_、および_>_)に分割する単純なバリアントがあります。この場合、このバリアントのパフォーマンスはO(n)です。ピボットを選択する代わりに、_0_から_pivotIndex-1_および_pivotIndex+1_からnにスワップし、繰り返します。ピボットに等しいすべてのものを「中間」パーティションにスワップします(これは、すべての同一入力の場合、常にそれ自体とのスワップ、つまりノーオペレーションを意味します)。つまり、この特定のケースでは、呼び出しスタックの深さは1つだけです。比較とスワップは発生しません。この変種は、少なくともLinuxの標準ライブラリに組み込まれていると思います。

33
tobyodavies

クイックソートのパフォーマンスは、ピボットの選択によって異なります。選択したピボットが中央値要素に近いほど、クイックソートのパフォーマンスは向上します。

この特定のケースでは、幸運です。すべての値が同じであるため、選択するピボットは常にa中央値になります。したがって、クイックソートのパーティションステップでは要素を交換する必要がなく、2つのポインターがちょうど真ん中で出会うことになります。したがって、2つのサブ問題のサイズはちょうど半分になり、完全なO(n log n)が得られます。

もう少し具体的に言うと、これはパーティションステップがどの程度適切に実装されているかによって異なります。ループ不変条件は、小さい要素が左側のサブ問題にあり、大きい要素が右側のサブ問題にあることを確認する必要があるだけです。パーティションの実装が等しい要素を決して交換しないという保証はありません。しかし、それは常に不必要な作業であるため、巧妙な実装でそれを行うべきではありません。leftおよびrightポインターは、ピボットのそれぞれの反転を検出しません(つまり、*left > pivot && *right < pivot)したがって、leftポインターがインクリメントされ、rightポインターがステップごとにデクリメントされ、最終的に途中で合流して、サイズn/2のサブ問題が生成されます。

5
ltjax

それは特定の実装に依存します。

他の要素がピボットに対してどこに移動するかを決定するための比較が1種類(≤または<)しかない場合、それらはすべてパーティションの1つに入り、O(を取得します。 n2)パフォーマンス。問題のサイズは各ステップで1つだけ減少するためです。

アルゴリズム ここにリストされています は例です(添付の図は別のアルゴリズムのものです)。

2種類の比較がある場合、たとえば、2ポインター実装の場合のように、左側の要素の<と右側の要素の>は、andポインタを段階的に移動するように注意すると、完全なO(nログが得られる可能性があります)n)パフォーマンス。これは、等しい要素の半分が2つのパーティションに均等に分割されるためです。

上記のリンクの図は、ポインターを段階的に移動しないアルゴリズムを使用しているため、パフォーマンスが低下します(「いくつかの固有の」ケースを参照してください)。

したがって、アルゴリズムを実装するときにこの特殊なケースを念頭に置いているかどうかによって異なります。

実際の実装では、より広範な特殊なケースを処理することがよくあります。パーティショニングステップにスワップがない場合、データはほぼソートされていると想定し、挿入ソートを使用します。これにより、さらに優れたO(n )すべての等しい要素の場合。

2
aaz

tobyodaviesは適切なソリューションを提供しています。ケースを処理し、すべてのキーが等しいO(n)時間で終了します。これは、オランダの国旗問題で行うのと同じ分割です。

http://en.wikipedia.org/wiki/Dutch_national_flag_problem

プリンストンからのコードの共有

http://algs4.cs.princeton.edu/23quicksort/Quick3way.Java.html

1
rusty

双方向パーティショニングアルゴリズムを実装すると、すべてのステップで配列が半分になります。これは、同一のキーが検出されると、スキャンが停止するためです。その結果、各ステップで、パーティショニング要素がサブ配列の中央に配置されるため、後続のすべての再帰呼び出しで配列が半分になります。現在、このケースは、~N lg N比較を使用してN個の要素の配列をソートするマージソートのケースに似ています。重複キーのエルゴ、クイックソートの従来の2ウェイパーティショニングアルゴリズムは~N lg N比較を使用するため、線形アプローチに従います。

0
Farhan Haque