私の一般的な QuickSort アルゴリズムには、原因を特定できない 2 つの問題があります。
- リストが正しくソートされないことがあります
- 20,000 項目を超えるリストを扱う場合、値が何度も繰り返されるとスタック オーバーフローが発生することがよくあります (これは実際の問題ではないと思います)。
リストが正しくソートされないことはかなりまれであるため、リストは適度に大きくする必要があります。以下の貼り付けは、インストールされているソフトウェアの 172 要素のリストの最初の 13 要素を示しています。最初の列は最初の並べ替え後の出力を示し、2 番目の列は 2 番目の並べ替えの出力を示しています。
Adobe AIR 7-Zip 4.65 (x64 edition)
7-Zip 4.65 (x64 edition) Adobe AIR
Adobe Community Help Adobe Community Help
Adobe Encore CS4 Codecs Adobe Encore CS4 Codecs
Adobe Media Encoder CS4 Exporter Adobe Media Encoder CS4 Exporter
Adobe Media Encoder CS4 Importer Adobe Media Encoder CS4 Importer
Adobe Media Player Adobe Media Player
Adobe Reader X (10.1.0) Adobe Reader X (10.1.0)
Adobe Setup Adobe Setup
Adobe Setup Adobe Setup
Apple Application Support Adobe Setup
Adobe Setup Apple Application Support
Apple Mobile Device Support Apple Mobile Device Support
... ...
ご覧のとおり、最初に並べ替えたときに、いくつかの不適切な動作があり、別の並べ替えを行うことで修正されます。
スタック オーバーフローに関する 2 つ目の問題は、古い Windows イベント ログ リストを並べ替えると発生します。4 週間にまたがる 52,000 の日付を並べ替えると、うまくいきます。しかし、繰り返しが多い 52,000 個の ID 番号を並べ替えると、システムがクラッシュするずっと前に、スタック サイズが 998 アイテムになります。ほとんどが「gupdate」である列で30,000の長いリストをソートした場合にも発生します。
今、私が見る限り、スタック カウントは log2(n) である必要があります。これは、スタックに追加された量が、実行された半分の量に等しいためです。
この効果を助けるためにピボットをランダムにしましたが、大きな違いはありませんでした。
Log2(60000) = 16. スタック オーバーフローには十分ではありません。
これは問題のコードです:
private static void QuickSortFunction<T>(T[] array, int start, int end, Comparer<T> comparer)
{
if (end - start >= 1)
{
int leftPtr, rightPtr, pivot;
Random random = new Random();
pivot = random.Next(start, end);
Swap(array, pivot, start);
pivot = start;
leftPtr = start + 1;
rightPtr = end;
while (leftPtr < rightPtr)
{
while ((comparer.Compare(array[leftPtr], array[pivot])) <= 0 && (leftPtr < rightPtr))
{
leftPtr++;
}
while ((comparer.Compare(array[rightPtr], array[pivot])) >= 0 && (leftPtr <= rightPtr))
{
rightPtr--;
}
if (leftPtr < rightPtr)
{
Swap(array, leftPtr, rightPtr);
}
}
Swap(array, pivot, rightPtr);
pivot = rightPtr;
QuickSortFunction(array, start, pivot - 1, comparer);
QuickSortFunction(array, pivot + 1, end, comparer);
}
}
private static void Swap<T>(T[] array, int pointer1, int pointer2)
{
T temp = array[pointer1];
array[pointer1] = array[pointer2];
array[pointer2] = temp;
}
興味のある人にとっては、これは順不同のグリッチの修正です。基本的に、2 要素の配列が正しくない場合、認識に失敗していました。{E, B} などは、独自のピボットを参照しないため、変更されません。
if (end - start >= 1)
{
int leftPtr, rightPtr, pivot;
Random random = new Random();
pivot = random.Next(start, end);
Swap(array, pivot, start);
pivot = start;
leftPtr = start;
rightPtr = end;
while (leftPtr < rightPtr)
{
while ((comparer.Compare(array[leftPtr], array[pivot])) < 0 && (leftPtr < rightPtr))
{
leftPtr++;
}
while ((comparer.Compare(array[rightPtr], array[pivot])) >= 0 && (leftPtr < rightPtr))
{
rightPtr--;
}
if (leftPtr < rightPtr)
{
Swap(array, leftPtr, rightPtr);
}
}
Swap(array, pivot, rightPtr);
pivot = rightPtr;
QuickSortFunction(array, start, pivot - 1, comparer);
QuickSortFunction(array, pivot + 1, end, comparer);
}
スタック オーバーフローの解決策を書いたら更新します。