私はクイックソートについて学び、クイックソートが困難なさまざまな配列を説明したいと思います。私が念頭に置いているクイックソートには、初期ランダムシャッフルはなく、2つのパーティションがあり、中央値を計算しません。
これまでに3つの例を考えました。
[1,2,3,4,5,6,7,8,9,10] - when the array is sorted
[10,9,8,7,6,5,4,3,2,1] - when the array is reversed
[1,1,1,1,1,1,1,1,1,1] - when the array is the same values
[1,1,1,2,2,2,3,3,3,3] - when there are few and unique keys
たとえば、これについてはよくわかりません。
[1,3,5,7,9,10,8,6,4,2]
では、クイックソートが(ほぼ)理想的である配列と比較して困難な配列は何になるのでしょうか。
すべてのソートアルゴリズムには最悪のケースがあり、多くの場合最悪のケースは本当に悪いので、テストする価値があります。問題は、基本的なアルゴリズムを知っているからといって、最悪のケースが1つもないということです。
一般的な最悪のケースは次のとおりです。逆順にソートされます。ほぼソートされた、1つの順不同要素。すべての値は同じです。最初(または最後)が高い(または低い)以外はすべて同じです。私たちはかつて、最悪のケースが特定の鋸歯状パターンである種類を持っていました。これは予測が非常に困難でしたが、実際には非常に一般的でした。
クイックソートの最悪のケースは、可能な限り最悪のピボットを常に選択して、パーティションの1つに要素が1つしかないケースです。ピボットが最初の要素である場合(悪い選択)、既に並べ替えられたデータまたは逆に並べ替えられたデータが最悪のケースです。中央値が3のピボットデータがすべて同じか、最初と最後だけが異なる場合、トリックは異なります。
クイックソートの場合、平均的な複雑度はnlognで、最悪のケースはn ^ 2です。最悪のケースの動作をトリガーする価値があるのは、これが最大の再帰深度を生成するケースでもあるためです。単純な実装の場合、再帰の深さはnになる可能性があり、スタックオーバーフローをトリガーする可能性があります。同様の理由で、他の極端な状況(最良のケースを含む)をテストすることは価値があります。
アルゴリズムは、ランダム化されたピボットを使用してほとんどの悪いケースから脱出し、ピボットに等しい連続要素をパーティション分割および非対称検索から除外します。要素を前方に検索しピボット以上、後方に要素を検索します未満ピボット。
MichaelTに感謝します。非対称検索は[2,1,2,1,2,1,2,1]を解決するために考案されました。
次の結果は、関数qsort_random()によって生成されます。 N = 100,000
usec call compare copy pattern
80132 62946 1971278 877143 random
47326 57578 1606067 215155 sorted : 0,1,2,3,...,n-1
49927 63578 1628883 338715 sorted in reverse : n-1,n-2,...,2,1,0
55619 63781 1596934 377330 nearly reverse : n-2,n-1,n-4,n-3,...,2,3,0,1
54714 66667 1611454 290392 median-3-killer : n-1,0,1,2,...,n-2
1491 1 99999 4 all values the same : n,n,n,...
1577 1 99999 4 first is higher : n,1,1,1,...
2778 2 156159 10 last is lower : n,n,n,...,n,1
2994 3 199996 100009 a few data : n,...,n,1,...,1
3196 3 199996 50012 zigzag : n,1,n,1,...,n,1
917796 56284 67721985 673356 valley(sawtooth?) : n-1,n-3,...,0,...,n-4,n-2
ほとんどの場合、ランダムパターンよりも高速です。谷のパターンは、ほとんどのピボット選択の悪いケースです。
qsort(3) usec = 14523 call = 0 compare = 884463 copy = 0
qsort_head() usec = 138609 call = 99999 compare = 8120991 copy = 1214397
qsort_middle() usec = 664325 call = 99999 compare = 52928111 copy = 1036047
qsort_trad() usec = 118122 call = 99999 compare = 6476025 copy = 1337523
qsort_random() usec = 295699 call = 58806 compare = 19439952 copy = 732962
qsort_log2() usec = 66411 call = 63987 compare = 1597455 copy = 944821
qsort_log2()は、log2(N)要素のピボットを選択することにより、不正なケースからエスケープします。
qsort(3)使用GNUライブラリは、インデックスソートのマージソートです。
qsort_trad()は、最初、中間、最後の要素でピボットを選択します。
qsort_random()およびqsort_log2()はスワッピングを使用しません。
ソースCプログラムとスクリプトは github に投稿されます。