に感謝し、あなたのピボット値として三つの要素array[start]
、array[last]
とarray[(start + last + 1)/2]
の中央値を選択することによってこの問題を回避することができます。
int median_of_3(long* array, long start, long last)
{
long a = (start + last + 1)/2, b = start, c = last;
if (array[c] < array[a]) swap(array[c], array[a]);
if (array[b] < array[a]) swap(array[b], array[a]);
if (array[c] < array[b]) swap(array[c], array[b]);
return partition(array, start, last);
}
大きなスタック深度を避けるための追加の戦略は、どのパーティションが小さいかを再計算し、小さなパーティションを再帰的に呼び出すことです。その後、他のパーティションをループに最適化することができます(末尾再帰最適化)。
void quickSort(long* array, long start, long last)
{
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
quickSort(array, next_start[i], next_last[i]);
/*
* If the compiler does not optimize the tail call below into
* a loop, it is easy to do the optimization manually.
*/
quickSort(array, next_start[!i], next_last[!i]);
}
イントロスペクションは、大きなスタックの深さを避けるためにも使用できます。再帰呼び出し深度を追跡し、「深すぎる」場合は、マージソートやヒープソートなどの異なるソート戦略に安全に失敗します。これは現在、std::sort
によって使用されている動作です。
void introSortImpl(long* array, long start, long last, int depth)
{
if (--depth == 0) {
heapSort(array, start, last);
return;
}
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
introSortImpl(array, next_start[i], next_last[i], depth);
introSortImpl(array, next_start[!i], next_last[!i], depth);
}
void introspectionSort(long* array, long start, long last)
{
introSortImpl(array, start, last, log2(start - last) * 3);
}
[完了、最小限の例]をご提示ください(http://stackoverflow.com/help/mcve) – OldProgrammer
、10000要素の配列で、これは10000 - 深い再帰呼び出しになるだろう。帽子から数を引くと、それぞれの再帰呼び出しが64バイトを消費するとします(単純なスタックフレームの場合は妥当な見積もりに加えて、3つの 'long'引数)。これは、これは約640kbのスタックを取るように見えるだろう。Bill Gatesによると、誰もが十分であるはずだから、私はちょっとしたものだ。 –