LabASD - Alberi binari di ricerca

Moreno Marzolla

Ultimo aggiornamento: 2024-04-05

Fonte: https://www.pinterest.com/pin/binary-tree-funny--547961479663761031/
Fonte: https://www.pinterest.com/pin/binary-tree-funny--547961479663761031/

Il file bst.h contiene l’interfaccia della struttura dati Albero Binario di Ricerca (BST, binary search tree). Completare l’implementazione realizzando le funzioni indicate. È possibile definire ulteriori funzioni di supporto, se lo si ritiene necessario.

Un BST è definito dalla struttura:

typedef struct {
    BSTNode *root;
    int size;
} BST;

dove root punta alla radice dell’albero, oppure a NULL se l’albero è vuoto, e size è il numero di nodi. Il campo size consente di conoscere il numero di nodi dell’albero in tempo \(O(1)\), senza bisogno di contarli ogni volta; occorre però mantenere aggiornato tale valore durante le operazioni di inserimento e cancellazione.

Un nodo è definito dalla struttura BSTNode seguente:

typedef int BSTKey;

typedef struct BSTNode {
    BSTKey key;
    struct BSTNode *parent, *left, *right;
} BSTNode;

dove BSTKey è il tipo dell’informazione associata ai nodi (di default è un intero), mentre parent, left e right sono puntatori al padre, al figlio sinistro e al figlio destro; nel nodo radice si avrà parent == NULL. Un esempio è mostrato nella Figura 1.

Figura 1: Esempio di albero binario di ricerca
Figura 1: Esempio di albero binario di ricerca

Le operazioni di inserimento e ricerca non dovrebbero presentare difficoltà; personalmente ritengo più semplice implementarle in forma ricorsiva, ma è possibile realizzarle in forma iterativa come visto a lezione. Le implementazioni ricorsive richiederanno la definizione di ulteriori funzioni a supporto.

In questa implementazione dei BST occorre mantenere la seguente proprietà di ordine:

Proprietà di un BST. Sia \(x\) un nodo di un BST. Se \(y\) è un nodo del sottoalbero sinistro di \(x\), allora si avrà \(y.\mathit{key} < x.\mathit{key}\); se \(z\) è un nodo del sottoalbero destro di \(x\), allora si avrà \(z.\mathit{key} > x.\mathit{key}\).

Si noti che non sono ammesse chiavi duplicate.

L’operazione più complessa è la cancellazione. Esistono numerose varianti della procedura di cancellazione, ma si suggerisce di seguire quella descritta nel libro: se il nodo da cancellare ha due figli, si rimpiazza la chiave in esso contenuta con la chiave minima del sottoalbero destro. In alternativa si può anche rimpiazzare la chiave con il massimo del sottoalbero sinistro.

La funzione bst_pretty_print() (da lasciare per ultima perché è la meno importante) deve stampare il contenuto di un BST in modo “leggibile” ruotando l’output di 90 gradi.

Ad esempio, l’albero

         12
        /  \
       5    55
      / \
    -3   9

dovrebbe essere stampato come:

   55
12
      9
   5
      -3

Il programma bst-main.c legge una sequenza di comandi da un file il cui nome va specificato come unico parametro sulla riga di comando. I comandi sono descritti nella Tabella 1, dove k è una chiave intera.

Tabella 1: Comandi nel file di input
Comando Significato
+ k Inserisce la chiave k, se non presente
- k Cancella il nodo contenente k, se presente
? k Verifica se la chiave k è presente
s Stampa il numero di nodi nell’albero
h Stampa l’altezza dell’albero
p Stampa il contenuto dell’albero

Un esempio di file di input è bst.in; l’output corrispondente è bst.out.

Per compilare:

    gcc -std=c90 -Wall -Wpedantic bst.c bst-main.c -o bst-main

Per eseguire in ambiente Linux/MacOSX:

    ./bst-main bst.in

Per eseguire in ambiente Windows:

    .\bst-main bst.in

Per approfondire

File