web-dev-qa-db-fra.com

Pourquoi QuickSort utilise O(log(n)) espace supplémentaire?

J'ai implémenté l'algorithme de tri rapide ci-dessous. En ligne, j'ai lu qu'il a un espace requis de O (log (n)). pourquoi est-ce le cas? Je ne crée pas de structures de données supplémentaires.

Est-ce parce que ma récursivité utilisera un espace supplémentaire sur la pile? Si tel est le cas, est-il possible de le faire avec moins de mémoire en ne le faisant pas récursif (au lieu de le rendre itératif)?

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

Correct, l'espace supplémentaire est constitué des trames de pile log (n). D'après article Wikipedia de Quicksort :

Il existe une version plus complexe qui utilise un algorithme de partition sur place et peut réaliser le tri complet en utilisant l'espace O (log n) (sans compter l'entrée) en moyenne (pour la pile d'appels) .

Alors que vous pourriez implémenter le tri rapide de manière itérative (c'est-à-dire en utilisant une boucle au lieu de la récursivité), vous auriez alors besoin de maintenir une pile auxiliaire, car Quicksort a deux récursif appels et pas un seul.

Enfin, comme d'autres réponses l'ont souligné, O(log(n)) est pour presque toutes les applications pratiques très, très petit. Chaque facteur constant, comme la surcharge de votre structure de données, aura un impact plus important sur l'utilisation de la mémoire.

40
rolve

Pour vous débarrasser de l'appel récursif, vous devrez utiliser une pile dans votre code, et elle occupera toujours l'espace log(n).

4
japreiss

Si vous lisez plus loin dans l'article Wikipédia, vous trouverez plus discussion approfondie de la complexité de l'espace . En particulier, ils écrivent:

Le tri rapide avec partitionnement in situ et instable utilise uniquement un espace supplémentaire constant avant d'effectuer un appel récursif. Quicksort doit stocker une quantité constante d'informations pour chaque appel récursif imbriqué. Étant donné que le meilleur des cas effectue au plus O (log n) appels récursifs imbriqués, il utilise l'espace O (log n). Cependant, sans l'astuce de Sedgewick pour limiter les appels récursifs, dans le pire des cas, le tri rapide pourrait effectuer O(n) appels récursifs imbriqués et nécessiter O(n) auxiliaire espace.

En pratique, la mémoire O (log n) n'est rien. Par exemple, si vous deviez trier 1 milliard d'ints, leur stockage nécessiterait 4 Go, mais la pile ne nécessiterait que 30 trames de pile, soit quelque chose comme 40 octets, soit environ 1 200 octets au total.

3
meriton

Oui, c'est à cause des trames de pile, et oui, il peut être possible de le convertir en un algorithme itératif en faisant quelque chose de très intelligent (bien que rien ne me vienne immédiatement). Mais pourquoi? O(log(n)) l'espace n'est presque rien. Pour référence, même si vous avez un tableau de la taille maximale autorisée par Java, c'est 2 ^ 31 éléments, soit environ 8 Go. Quicksort nécessiterait 31 trames de pile. Ballpark, peut-être 100 octets par trame, donc 3 Ko au total, ce qui n'est rien comparé à la mémoire du tableau réel.

En réalité, presque chaque fois que quelque chose est O (log (n)), c'est à peu près la même chose que constante.

1
Joe K

Désolé d'avoir relancé cette ancienne question, mais je viens de trouver une réponse entièrement différente (mais légèrement idiote) à votre question, sur planetmath.org :

Tout algorithme de tri qui fonctionne sur un tableau contigu requiert [~ # ~] o [~ # ~] ⁢ ( log ⁡n) espace supplémentaire, car il s'agit du nombre de morsures [ sic] nécessaires pour représenter un index dans le tableau.

1
rolve