Algoritmo di ordinamento heap (con codice in Python e C++)

Cos'è l'algoritmo di ordinamento heap?

Heap Sort è uno degli algoritmi di ordinamento più popolari e più veloci. È costruito sulla struttura dati completa dell'albero binario. Cercheremo l'elemento massimo e lo metteremo in primo piano per l'heap massimo. Lo metteremo sul nodo genitore dell'albero binario.

Diciamo che viene fornito un array, dati = [10,5, 7, 9, 4, 11, 45, 17, 60].

Nell'array, se l'indice i-esimo (i=0,1,2,3 …) è un nodo genitore, allora (2i+1) e (2i+2) saranno i figli sinistro e destro. La creazione di un albero binario completo con questo array sarà simile a questa:

Algoritmo di ordinamento dell'heap

Eseguiremo il processo heapify dall'inizio alla fine dell'array. Inizialmente, se convertiamo l'array in un albero, apparirà come sopra. Possiamo vedere che non mantiene alcuna proprietà heap (min-heap o max heap). Otterremo l'array ordinato eseguendo il processo heapify per tutti i nodi.

Applicazione dell'ordinamento heap

Ecco alcuni utilizzi dell'algoritmo di ordinamento dell'heap:

  • La costruzione di "code prioritarie" richiede l'ordinamento dell'heap. Perché heapsort mantiene l'elemento ordinato dopo ogni inserimento.
  • La struttura dei dati heap è efficiente nel trovare kth elemento più grande in un dato array.
  • Il kernel Linux utilizza l'ordinamento heap come impostazione predefinita algoritmo di ordinamento poiché ha complessità spaziale O(1).

Crea ordinamento heap con l'esempio

Qui costruiremo un heap massimo dal seguente albero binario completo.

Crea ordinamento heap con l'esempio

I nodi foglia sono 17, 60, 4, 11 e 45. Non hanno alcun nodo figlio. Ecco perché sono nodi foglia. Quindi, avvieremo il metodo heapify dal loro nodo padre. Ecco i passaggi:

Passo 1) Seleziona il sottoalbero più a sinistra. Se i nodi figli sono maggiori, scambia il nodo genitore con il nodo figlio.

Qui il nodo genitore è 9. E i nodi figli sono 17 e 60. Poiché 60 è il più grande, 60 e 9 verranno scambiati per mantenere il heap max.

Crea ordinamento heap con l'esempio

Passo 2) Ora, il sottoalbero più a sinistra è heapificato. Il nodo genitore successivo è 7. Questo nodo genitore ha due nodi figli e il più grande è 45. Quindi, 45 e 7 verranno scambiati.

Crea ordinamento heap con l'esempio

Crea ordinamento heap con l'esempio

Passo 3) I nodi 60 e 4 hanno il nodo genitore 5. Poiché “5” è più piccolo del nodo figlio 60, verrà scambiato.

Crea ordinamento heap con l'esempio

Crea ordinamento heap con l'esempio

Passo 4) Ora, il nodo 5 ha il nodo figlio 17,9. Ciò non mantiene la proprietà heap massima. Quindi, 5 verrà sostituito con 17.

Crea ordinamento heap con l'esempio

Passo 5) Il nodo 10 verrà scambiato con 60, quindi con 17. Il processo sarà simile al seguente.

Crea ordinamento heap con l'esempio

Crea ordinamento heap con l'esempio

Passo 6) Fino al passaggio 5 abbiamo creato l'heap massimo. Ogni nodo genitore è più grande dei suoi nodi figli. Il nodo radice ha il valore massimo (60).

Nota: Per creare l'array ordinato, dobbiamo sostituire il nodo con valore massimo con il suo successore.

Questo processo si chiama "estrarre massimo”. Poiché 60 è il nodo massimo, fisseremo la sua posizione sull'indice 0 e creeremo l'heap senza il nodo 60.

Crea ordinamento heap con l'esempio

Crea ordinamento heap con l'esempio

Passo 7) Quando viene rimosso 60, il valore massimo successivo è 45. Eseguiremo nuovamente il processo "Estrai Max" dal nodo 45.

Questa volta otterremo 45 e sostituiremo il nodo radice con il suo successore 17.

Dobbiamo esibirci”Estrai massimo" finché tutti gli elementi non saranno ordinati.

Dopo aver eseguito questi passaggi fino ad estrarre tutti i valori massimi, otterremo il seguente array.

Crea ordinamento heap con l'esempio

Cos'è l'heap binario?

Un heap binario è una sorta di completo albero binario struttura dati. In questo tipo di struttura ad albero, il nodo genitore è maggiore o minore dei nodi figli. Se il nodo genitore è più piccolo, l'heap è chiamato "Min Heap" e se il nodo genitore è più grande, l'heap è chiamato "Max Heap".

Ecco alcuni esempi di heap minimo e heap massimo.

Heap minimo e Heap massimo
Heap minimo e Heap massimo

Nella figura sopra, se noti il ​​"Min Heap", il nodo genitore è sempre più piccolo dei suoi nodi figli. In cima all'albero troviamo il valore più piccolo, 10.

Allo stesso modo, per il "Max Heap", il nodo genitore è sempre più grande dei nodi figli. L'elemento massimo è presente nel nodo head per il "Max Heap".

Che cosa è “Heapify”?

"Heapify" è il principio dell'heap che assicura la posizione del nodo. In Heapify, un heap massimo mantiene sempre una relazione con il padre e il figlio, e cioè il nodo padre sarà più grande dei nodi figlio.

Ad esempio, se viene aggiunto un nuovo nodo, dobbiamo rimodellare l'heap. Tuttavia, potremmo dover cambiare o scambiare i nodi o riorganizzare l'array. Questo processo di rimodellazione di un heap è chiamato "heapify".

Ecco un esempio di come funziona heapify:

Aggiunta di un nuovo nodo e Heapify
Aggiungere un nuovo nodo e heapify

Ecco i passaggi per heapify:

Passo 1) Aggiunto il nodo 65 come figlio destro del nodo 60.

Passo 2) Controlla se il nodo appena aggiunto è maggiore del genitore.

Passo 3) Poiché è più grande del nodo genitore, abbiamo scambiato il figlio destro con il suo genitore.

Come costruire l'heap

Prima di costruire l'heap o di heapificare un albero, dobbiamo sapere come lo memorizzeremo. Poiché l'heap è un albero binario completo, è meglio usare un schieramento per contenere i dati dell'heap.

Diciamo che un array contiene un totale di n elementi. Se l'indice "i" è un nodo genitore, il nodo sinistro sarà all'indice (2i+1)e il nodo destro sarà in indice (2i+2). Supponiamo che l'indice dell'array inizi da 0.

Utilizzando questo, memorizziamo un heap massimo in un array simile al seguente:

Rappresentazione basata su array dell'heap massimo
Rappresentazione basata su array dell'heap massimo

L'algoritmo heapify mantiene la proprietà heap. Se il genitore non ha il valore estremo (più piccolo o più grande), verrà scambiato con il nodo figlio più estremo.

Ecco i passaggi per heapizzare un heap massimo:

Passo 1) Inizia dal nodo foglia.

Passo 2) Trova il massimo tra genitore e figli.

Passo 3) Scambia i nodi se il nodo figlio ha un valore maggiore del genitore.

Passo 4) Sali di un livello.

Passo 5) Segui i passaggi 2,3,4 fino a raggiungere l'indice 0 o ordinare l'intero albero.

Ecco lo pseudo-codice per heapify ricorsivo (heap massimo):

def heapify():
  input→ array, size, i
  largest = i
  left = 2*i + 1
  right = 2*i + 2
if left<n and array[largest ] < array[left]:
  largest = left
if right<n and array[largest ] < array[right]:
  largest = right
If largest not equals i:
  swap(array[i],array[largest])
  heapify(array,n,largest)

Pseudo codice per l'ordinamento dell'heap

Ecco lo pseudo-codice per l'algoritmo di ordinamento dell'heap:

Heapify(numbers as an array, n as integer, i as integer):
  largest = i
  left = 2i+1
  right= 2i+2
if(left<=n) and (numbers[i]<numbers[left])
  largest=left
if(right<=n) and (numbers[i]<numbers[right])
  largest=right
if(largest  != i)
  swap(numbers[i], numbers[largest])
  Heapify(numbers,n,largest)
HeapSort(numbers as an array):
  n= numbers.size()
for i in range n/2 to 1
  Heapify(numbers,n,i)
for i in range n to 2
  Swap numbers[i] with numbers[1]
  Heapify(numbers,i,0)

Esempio di codice di ordinamento heap in C++

#include <iostream>
using namespace std;
void display(int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        cout << arr[i] << "\t";
    }
    cout << endl;
}
void heapify(int numbers[], int n, int i)
{
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && numbers[left] < numbers[largest])
    {
        largest = left;
    }
    if (right < n && numbers[right] < numbers[largest])
    {
        largest = right;
    }
    if (largest != i)
    {
	//uncomment the following line to see details in output
        //cout<<"Swapping "<< numbers[i]<< " and "<<numbers[largest]<<endl;
        swap(numbers[i], numbers[largest]);
        heapify(numbers, n, largest);
    }
}
void heapSort(int numbers[], int n)
{
    for (int i = n/2 - 1; i >= 0; i--)
    {
        heapify(numbers, n, i);
//uncomment the following line to see details in output
 //cout<<"Heapify:\t";
  //display(numbers,n);
    }
    for (int i = n - 1; i >= 0; i--)
    {
        swap(numbers[0], numbers[i]);
        heapify(numbers, i, 0);
    }
}
int main()
{
    int numbers[] = { 10,5, 7, 9, 4, 11, 45, 17, 60};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    cout<<"Initial Array:\t";
    display(numbers,size);
    heapSort(numbers, size);
    cout<<"Sorted Array (descending order):\t";
    display(numbers, size);
}

Produzione:

Initial Array:  10      5       7       9       4       11      45      17      60
Sorted Array (descending order):  60      45      17      11      10      9       7       5       4

Esempio di codice di ordinamento heap in Python

def display(arr):
    for i in range(len(arr)):
    print(arr[i], end = "\t")
print()
def heapify(numbers, n, i):
    largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and numbers[left] < numbers[largest]:
    largest = left
if right < n and numbers[right] < numbers[largest]:
    largest = right
if largest != i:
    numbers[i], numbers[largest] = numbers[largest], numbers[i]
heapify(numbers, n, largest)
def heapSort(items, n):
    for i in range(n //2,-1,-1):
        heapify(items, n, i) for i in range(n - 1, -1, -1):
        items[0], items[i] = items[i], items[0] heapify(items, i, 0) numbers = [10, 5, 7, 9, 4, 11, 45, 17, 60] print("Initial List:\t", end = "") display(numbers) print("After HeapSort:\t", end = "") heapSort(numbers, len(numbers)) display(numbers)

Produzione:

Initial List:   10      5       7       9       4       11      45      17      60
After HeapSort: 60      45      17      11      10      9       7       5       4

Analisi della complessità temporale e spaziale di Heap Sort

Ci sono complessità temporale e complessità spaziale che possiamo analizzare per l'ordinamento heap. Per la complessità temporale abbiamo i seguenti casi:

  1. Caso migliore
  2. Caso medio
  3. Caso peggiore

L'heap è implementato su un albero binario completo. Quindi, al livello più basso dell'albero binario, ci sarà il numero massimo di nodi. Se il livello inferiore ha n nodi, il livello superiore avrà n/2 nodi.

Analisi della complessità temporale e spaziale

In questo esempio, il livello 3 ha quattro elementi, il livello 2 ha due elementi e il livello 1 ha un elemento. Se è presente un numero totale di n elementi, l'altezza o il livello totale sarà Log2(N). Pertanto, l'inserimento di un singolo elemento potrebbe richiedere un massimo di iterazioni Log(n).

Quando vogliamo prendere il valore massimo dall'heap, prendiamo semplicemente il nodo radice. Poi, eseguiamo di nuovo heapify. Ogni heapify prende Log2(N) tempo. L'estrazione del massimo richiede tempo O(1).

migliori Complessità temporale del caso per l'algoritmo di ordinamento heap

Quando tutti gli elementi sono già ordinati nell'array, ci vorrà O(n) tempo per costruire l'heap. Perché se l'elenco è ordinato, l'inserimento di un elemento richiederà il tempo costante O(1).

Quindi, nel migliore dei casi, ci vorrà O (n) tempo per creare un heap massimo o minimo.

Complessità temporale media del caso per l'algoritmo di ordinamento heap

L'inserimento di un elemento o l'estrazione di un costo massimo richiede tempo O(log(n)), quindi la complessità temporale media per l'algoritmo di ordinamento heap è O(nlog(n)).

Complessità temporale del caso peggiore per l'algoritmo di ordinamento heap

Similmente al caso medio, nello scenario peggiore, potremmo eseguire heapify n volte. Ogni heapify costerà O(log(n)) di tempo. Quindi, la complessità temporale del caso peggiore sarà O(nlog(n)).

Complessità spaziale per l'algoritmo di ordinamento heap

Heap sort è un algoritmo progettato in loco. Ciò significa che non è necessaria memoria extra o temporanea per eseguire l'attività. Se osserviamo l'implementazione, noteremo che abbiamo utilizzato swap() per eseguire lo scambio dei nodi. Non è stato necessario nessun altro elenco o array. Quindi, la complessità dello spazio è O(1).