Menu
Coddy logo textTech

Heap (heap binario)

Ultimo aggiornamento

Un heap binario è un albero binario completo che tiene alla radice il valore più piccolo (min-heap) o più grande (max-heap). Questa visualizzazione è un min-heap: ogni genitore è minore o uguale ai suoi figli. Per aggiungere un valore lo metti nella prossima posizione libera, poi lo "fai risalire" (sift-up) scambiandolo con il genitore finché è più piccolo, fino a quando la proprietà di heap torna a valere. Premi play qui sopra per vedere ogni nuovo valore risalire fino al suo posto.

Dato che un heap è un albero completo, si memorizza in modo compatto in un array: i figli del nodo i stanno in 2i+1 e 2i+2. Inserimento e rimozione del minimo sono O(log n) (un solo percorso dalla radice a una foglia), mentre leggere il minimo è O(1): esattamente ciò che serve a una coda di priorità.

Complessità temporale e spaziale

OperazioneComplessitàNote
Leggere min/maxO(1)È sempre la radice
Inserimento (push)O(log n)Sift-up lungo un percorso
Rimozione min/maxO(log n)Sift-down lungo un percorso
Costruzione dell'heapO(n)Heapify tutto in una volta
SpazioO(n)Basato su array, senza puntatori

Passo dopo passo (push)

PassoCosa succede
1Aggiungi il nuovo valore in fondo (prossima foglia libera).
2Confrontalo con il suo genitore.
3Se è più piccolo (min-heap), scambialo verso l'alto.
4Ripeti finché non è più piccolo del genitore o raggiunge la radice.

Esempio svolto

Costruzione di un min-heap inserendo [5, 3, 8, 1, 4] un valore alla volta:

PushArray dopo il sift-upAzione
5[5]Il primo valore diventa la radice.
3[3, 5]3 < genitore 5, quindi sale fino alla radice.
8[3, 5, 8]8 > genitore 5, quindi resta una foglia.
1[1, 3, 8, 5]1 < genitore 5, scambia; poi 1 < genitore 3, scambia fino alla radice.
4[1, 3, 8, 5, 4]4 > genitore 3, quindi resta lì; il minimo 1 rimane alla radice.

Quando usare un heap

Usalo quandoEvitalo quando
Ti serve ripetutamente l'elemento più piccolo o più grande di un insieme che cambia.Devi cercare valori arbitrari, non solo l'estremo: usa un BST o un hash set.
Stai implementando una coda di priorità per Dijkstra, A* o uno scheduler.Ti servono i dati sempre completamente ordinati.
Vuoi inserimento e rimozione del minimo O(log n) con una struttura compatta ad array.Ti serve cercare o rimuovere velocemente un elemento specifico (non la radice).
Devi unire un flusso di elementi ed estrarli per priorità.L'insieme di dati è minuscolo e una scansione lineare è più semplice e abbastanza veloce.

Codice Heap (Priority Queue)

Un'implementazione di Heap (Priority Queue) pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice Heap (Priority Queue) in Python

Python
1class MinHeap:2    def __init__(self):3        self.data = []4
5    def push(self, value):6        # Append at the end, then bubble up to restore order7        self.data.append(value)8        i = len(self.data) - 19        while i > 0:10            parent = (i - 1) // 211            if self.data[parent] <= self.data[i]:12                break13            self.data[i], self.data[parent] = self.data[parent], self.data[i]14            i = parent15
16    def pop(self):17        # Move the last leaf to the root, then sift it down18        top = self.data[0]19        last = self.data.pop()20        if self.data:21            self.data[0] = last22            self._sift_down(0)23        return top24
25    def _sift_down(self, i):26        n = len(self.data)27        while True:28            smallest = i29            left, right = 2 * i + 1, 2 * i + 230            if left < n and self.data[left] < self.data[smallest]:31                smallest = left32            if right < n and self.data[right] < self.data[smallest]:33                smallest = right34            if smallest == i:35                return36            self.data[i], self.data[smallest] = self.data[smallest], self.data[i]37            i = smallest38
39
40heap = MinHeap()41for value in [5, 3, 8, 1, 9, 2]:42    heap.push(value)43
44print("Heap array:     ", heap.data)45print("Popped in order:", [heap.pop() for _ in range(6)])
Esegui questo codice nel playground Python

Domande frequenti sull'heap

A cosa serve un heap?
Gli heap implementano le code di priorità, che fanno funzionare l'algoritmo del cammino minimo di Dijkstra, gli scheduler dei task e le simulazioni a eventi. Sono anche il motore dell'heapsort. Ogni volta che ti serve ripetutamente l'elemento più piccolo o più grande di un insieme che cambia, l'heap è lo strumento giusto.
Qual è la differenza tra un heap e un albero binario di ricerca?
Sono entrambi alberi binari, ma un BST mantiene un ordine completo da sinistra a destra (che rende possibile la ricerca ordinata), mentre un heap garantisce solo la relazione genitore-figlio (minimo o massimo alla radice). Un heap dà accesso O(1) al valore estremo; un BST dà una ricerca O(log n) per qualsiasi valore.
Perché un heap si memorizza in un array?
Dato che un heap è sempre un albero binario completo, i suoi nodi corrispondono perfettamente agli indici di un array: i figli dell'indice i stanno in 2i+1 e 2i+2, e il genitore in (i-1)/2. Così non devi memorizzare puntatori ai figli e ottieni ottime prestazioni di cache.
Un heap è la stessa cosa di un array ordinato?
No. Un heap garantisce solo che ogni genitore sia più piccolo (min-heap) o più grande (max-heap) dei suoi figli, quindi fratelli e cugini non hanno un ordine particolare. Un array ordinato è completamente ordinato ma un inserimento costa O(n), mentre un heap inserisce in O(log n) dando comunque accesso immediato al valore estremo.
Quando conviene usare un heap invece di ordinare un array?
Scegli un heap quando i dati continuano a cambiare e ti serve sempre e solo il minimo o il massimo corrente: inserire ed estrarre costano O(log n) ciascuno, invece di riordinare tutto l'array. Se hai un insieme di dati statico e vuoi tutti gli elementi in ordine, un singolo ordinamento O(n log n) è più semplice e spesso più veloce.
Costruire un heap da n elementi richiede O(n log n)?
No, se lo costruisci tutto in una volta. Inserire n elementi uno per uno è O(n log n), ma l'heapify dal basso verso l'alto, che fa il sift-down dall'ultimo genitore fino alla radice, richiede O(n) in totale, perché la maggior parte dei nodi sta vicino al fondo e scende solo di poco.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA