web-dev-qa-db-ja.com

QuickSortがO(log(n))余分なスペースを使用するのはなぜですか?

以下のクイックソートアルゴリズムを実装しました。オンラインで、O(log(n))のスペース要件があることを読みました。これはなぜですか?私は余分なデータ構造を作成していません。

私の再帰はスタック上の余分なスペースを使用するためですか?これが当てはまる場合、(反復的にするのではなく)再帰的にしないことで、より少ないメモリで実行できますか?

private static void quickSort (int[] array, int left, int right) {
    int index = partition(array, left, right);

    //Sort left half
    if (left < index - 1)
        quickSort(array, left, index - 1);

    //Sort right half
    if (index < right)
        quickSort(array, index , right);
}

private static int partition (int array[], int left, int right) {
    int pivot = array[(left + right) / 2]; //Pick pivot point
    while (left <= right) {
        //Find element on left that should be on right
        while (array[left] < pivot)
            left++;

        //Find element on right that should be on left
        while (array[right] > pivot)
            right--;

        //Swap elements and move left and right indices
        if (left <= right) {
            int temp = array[left];
            array[left] = array[right];
            array[right] = temp;
            left++;
            right--;
        }
    }
    return left;
}
32
Joey Franklin

余分なスペースはlog(n)スタックフレームです。 クイックソートのウィキペディアの記事 から:

インプレースパーティションアルゴリズムを使用し、平均でO(log n)スペース(入力をカウントしない)を使用して完全なソートを実現できる、より複雑なバージョンがあります(コールスタックの場合)

couldクイックソートを繰り返し実装する(つまり、再帰の代わりにループを使用する)一方で、Quicksortにはtwoがあるため、補助スタックを維持する必要があります1つだけではなく、再帰呼び出し。

最後に、他の答えが指摘しているように、O(log(n))はほとんどすべての実用的なアプリケーションにとって非常に、very小さい。データ構造のオーバーヘッドなどの一定の要因は、メモリ使用量に大きな影響を与えます。

40
rolve

再帰呼び出しを取り除くには、コードでスタックを使用する必要がありますが、それでもlog(n)スペースを占有します。

4
japreiss

ウィキペディアの記事をさらに読むと、さらに多くの スペースの複雑さの詳細な議論 が見つかります。特に、彼らは書きます:

インプレースパーティションと不安定パーティションを備えたクイックソートは、再帰呼び出しを行う前に一定の追加スペースのみを使用します。クイックソートは、ネストされた再帰呼び出しごとに一定量の情報を保存する必要があります。最良のケースでは、最大でO(log n)のネストされた再帰呼び出しが行われるため、O(log n)スペースが使用されます。ただし、Sedgewickの再帰呼び出しを制限するトリックがないと、最悪の場合、クイックソートはO(n)ネストされた再帰呼び出しを行い、O(n)補助スペース。

実際には、O(log n)メモリは何もありません。たとえば、10億のintをソートする場合、それらを格納するには4 GBが必要ですが、スタックに必要なスタックフレームは40バイト程度で、合計で約1200バイトです。

3
meriton

はい、それはスタックフレームのためです、そして、はい、非常に賢い何かをすることによってそれを反復アルゴリズムに変換することは可能かもしれません(すぐに何も私に来ていませんが)。しかし、なぜ? O(log(n))スペースはほとんどありません。参考までに、Javaで許可されている最大サイズの配列がある場合でも、2 ^ 31の要素、約8 GBです。クイックソートには31個のスタックフレームが必要になりますが、ボールパークは1フレームあたり100バイトでしょうか?合計3 KBで、実際のアレイのメモリと比較しても何もありません。

実際には、ほとんどの場合、何かがO(log(n))である場合、定数とほとんど同じです。

1
Joe K

この古い質問を復活させてすみませんが、 planetmath.org で、あなたの質問に対するまったく異なる(しかし少し馬鹿げた)答えを見つけました。

任意の連続した配列で動作するソートアルゴリズムには、[〜#〜] o [〜#〜]⁢(log⁡n)余分なスペース。これはバイト数[sic]で、インデックスを表すために必要です。配列。

1
rolve