- L'algoritmo quicksort in C -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario Un compilatore C, conoscenze basiche di C (in particolare macro, puntatori e ricorsione).
Spiegazione dettagliata di come funziona un'implementazione di quicksort in C

CODICE SORGENTE

#include <stdio.h>
#define N 12

/* Semplice macro che inverte i valori di due variabili */
#define   inverti(x, y)    { int t; t = x; x = y; y = t; }
/* Macro che ordina due variabili */
#define   ordina(x, y)     if (x > y) inverti(x, y)
/* Macro che ordina tre variabili */
#define   ordina3(x, y, z) ordina(x, y); ordina(x, z); ordina(y, z)

int   trova_perno(int *sinistra, int *destra, int *ptr_perno);
int   *dividi(int *sinistra, int *destra, int perno);
void  quicksort(int *sinistra, int *destra);

/* Funzione che ordina l'intervallo specificato */
void quicksort(int *sinistra, int *destra) {
    int *p, perno;
    
    /* Individuiamo il valore perno e lo mettiamo in perno (se possibile) */
    if (trova_perno(sinistra, destra, &perno)) {
        /* 
            Muoviamo gli elementi nell'array in modo che a sinistra di p
            risultino esserci solamente valori minori del valore perno, 
            maggiori o uguali a destra
        */
        p = dividi(sinistra, destra, perno);
        /* 
            Chiamiamo ricorsivamente quicksort sui due intervalli individuati
        */
        quicksort(sinistra, p - 1);
        quicksort(p, destra);
    }
}

/* Restituisce un valore che farà da perno, se possibile */
int trova_perno(int *sinistra, int *destra, int *ptr_perno) {
    int   a, b, c, *p;
    
    /* Prendiamo il primo, l'ultimo e l'elemento intermedio */
    a = *sinistra;
    b = *(sinistra + (destra - sinistra) / 2);
    c = *destra;
    /* Li ordiniamo in maniera crescente */
    ordina3(a, b, c);
    /* Se b non è uguale ad a lo prendiamo come elemento perno */
    if (a < b) {
        *ptr_perno = b;
        return 1;
    }
    /* Se c non è uguale a b lo prendiamo come elemento perno */
    if (b < c) {
        *ptr_perno = c;
        return 1;
    }
    
    /*
        Il primo, l'ultimo e l'elemento intermedio dell'array
        hanno tutti lo stesso valore: cerchiamo il primo con un 
        valore diverso a partire da sinistra 
    */
    for (p = sinistra + 1; p <= destra; ++p)
        if (*p != *sinistra) {
            *ptr_perno = (*p < *sinistra) ? *sinistra : *p;
            return 1;
        }
    /* 
        Tutti i valori dell'intervallo sono uguali, non è 
        possibile individuare un elemento perno
    */
    return 0;
}



/* 
    Divide l'intervallo specificato in due parti in modo che
    a destra di un certo elemento (viene determinato man mano
    e infine restituito) si trovino solamente valori maggiori
    o uguali al perno, a sinistra quelli minori.
    Per fare questo si parte dagli estremi dell'intervallo
    e ci si avvicina al centro lasciando immutati i valori
    minori del perno procedendo da sinistra e quelli maggiori
    o uguali al perno procedendo da destra, mentre si 
    invertono gli altri: ad esempio se il perno è 6 nella 
    sequenza 1 2 7 8 9 3 verranno scambiati di posizione il
    7 (primo elemento maggiore del perno a partire da sinistra)
    e il 3 (primo elemento minore del perno a partire da 
    destra). L'operazione viene ripetuta finché il puntatore
    di destra e quello di sinistra non si incontrano.
*/
int *dividi(int *sinistra, int *destra, int perno) {
    /* Finché i due puntatori non si incontrano */
    while (sinistra <= destra) {
        /*
            Individuiamo il primo elemento a partire
            da sinistra maggiore o uguale al perno
        */
        while (*sinistra < perno)
            ++sinistra;
        /*
            Individuiamo il primo elemento a partire
            da destra minore del perno
        */
        while (*destra >= perno)
            --destra;
        /* Se i due punti non coincidono */
        if (sinistra < destra) {
            /* Invertiamo le posizioni dei due valori */
            inverti(*sinistra, *destra);
            ++sinistra;
            --destra;
        }
    }
    /*
        Restituisce il puntatore all'elemento che divide
        l'intervallo secondo il criterio del perno
    */
    return sinistra;
}


int main(int argc, char *argv[]) {
    /* Un array composto da una sequenza di 12 valori casuali */
    int array[N] = {4,56,8,-2,5,-7,8,3,0,2,7,-1};
    int c1; /* Un contatore per stampare l'array */
    /* Stampiamo l'array prima dell'ordinamento */
    for (c1=0; c1 < N; c1++)
        printf("%d ", array[c1]);
    printf("\n");
    
    /* Ordiniamo l'array */
    quicksort(array, array + N - 1);
    
    /* Stampiamo l'array dopo l'ordinamento */
    for (c1=0; c1 < N; c1++)
        printf("%d ", array[c1]);
    printf("\n");
    
    return 0;
}

<< INDIETRO by VeNoM00