Progetto d`esame PAS A042 Informatica: Fondamenti e

Progetto d’esame PAS A042
Informatica: Fondamenti e Programmazione
Sessione di luglio 2014
Una struttura dati si dice dinamica se permette l’esecuzione di operazioni
che ne modficano la struttura o anche semplicemente la dimensione. Strutture dati dinamiche possono essere realizzate (o simulate) anche lavorando
con linguaggi di programmazione che non supportano allocazione e disallocazione di memoria dinamica.
In questo progetto ci concentreremo su strutture dinamiche organizzate
come alberi binari. Questi possono essere realizzati utilizzando la cosiddetta
soluzione dei vettori paralleli, cioè vettori con lo stesso numero di posizioni
e tali per cui, per ogni indice i, le informazioni memorizzate nelle posizioni
i-esime dei vettori si riferiscono tutte alla stessa entità (e dunque allo stesso
nodo dell’albero).
La seguente figura illustra un esempio di albero binario e la sua implementazione mediante tre array paralleli: un array che contiene le informazioni
(in questo esempio, semplicemente delle lettere) e due array che indicano le
posizioni dei nodi figli. Le posizioni vuote indicano assenza di informazione.
A
C
E
D
B
inf
1
A
2
B
3
C
left
6
9
2
right
3
7
4
5
D
6
E
7
G
8
9 10 11
F
5
G
F
Figure 1: Un esempio di albero binario rappresentato mediante array paralleli.
L’unica altra informazione necessaria è rappresentata da una semplice
variabile di tipo intero, che chiameremo root, che contiene l’indice della posione corrispondente alla radice dell’albero. Nel caso dell’esempio root = 1
perché la radice è memorizzata nella posizione 1 degli array paralleli. Si
noti che le posizioni occupate da informazione significativa non sono necessariamente contigue; alcune posizioni possono essere vuote (come la 4 e la
1
8 nell’albero di figura 1) a causa di sequenze di operazioni di inserzione e
cancellazione di elementi dalla struttura dati.
In questo progetto si chiede di definire e implementare una struttura
dati “albero binario dinamico” utilizzando la tecnica degli array paralleli.
Chiaramente, ogni struttura dati (e quindi anche un albero) è pensata e
realizzata per memorizzare informazione rilevante per una data applicazione.
Tuttavia, dal punto di vista della manipolazione algoritmica della struttura,
tale informazione gioca un ruolo marginale. Nel nostro caso supporremo
quindi che ogni nodo memorizzi semplicemente un singolo dato numerico.
Dal punto di vista squisitamente algoritmico ciò che conta sono invece le
informazioni che conferiscono una certa organizzazione ai dati (in questo caso,
gerarchica o, appunto, “ad albero”) e che consentono l’accesso ai medesimi o
la loro modifica. Proprio allo scopo di facilitare accesso e modifica, prevediamo che ogni nodo dell’albero contenga tre ulteriori campi : i due puntatori ai
nodi figli, come nel caso di figura 1, e un puntatore al nodo genitore, assente
nell’esempio di figura.
Possiamo dunque definire il singolo nodo dell’albero mediante la seguente
struttura logica:
struct nodo;
float inf;
↑nodo left,right;
↑nodo parent;
end
dove la notazione ↑nodo vuol indicare il tipo di dato “puntatore a nodo”.
Ovviamente, nell’implementazione concreta mediante array paralleli, una
variabile di questo tipo è semplicemente un intero.
La struttura dati deve “supportare” almeno le operazioni primitive descritte di seguito.
search Dato un numero intero i, restituisce l’indice del nodo corrispondente
(cioè la posizione k degli array paralleli tale che inf[k] = i); se i non è
presente nell’albero, allora restituisce 0.
insert Dato un numero intero i, inserisce un nuovo nodo nell’albero che
memorizza i nel campo informazione. Se i è già presente, l’operazione
corrisponde a no-op.
delete Dato l’indice k di un nodo, lo rimuove dall’albero. Dopo la rimozione
del nodo, la struttura dati risultante deve essere ancora un albero connesso.
2
save Salva l’albero in file binario dal nome scelto dall’utente, oppure predefinito se l’utente non sceglie alcun nome.
load Carica l’albero da un file binario dal nome scelto dall’utente, oppure
predefinito se l’utente non sceglie alcun nome. In caso di fallimento
dell’operazione, il precedente contenuto dell’albero deve rimanere inalterato, mentre in caso di successo il precedente contenuto dell’albero è
perso.
Utilizzando le operazioni primitive sopra descritte, si scrivano poi due
procedure che risolvono i seguenti problemi:
stampa ordinata Dato un albero T (cioè dato il puntatore alla radice
dell’albero), stampa tutti le informazioni numeriche in esso contenute
in ordine crescente;
minimo antenato comune dati due nodi x e y stampa l’informazione numerica contenuta nel nodo che è il minimo antenato comune di x e y,
cioè quel nodo z tale che: (1) z è antenato sia di x che di y, e (2) non
esiste un discendente di z che sia ancora antenato di x e y.
Nella risoluzione del secondo problema, si tenga conto del fatto che un nodo
è considerato antenato di se stesso, Questo vuol dire che se (ad esempio) x è
antenato di y, allora z = x, cioè x è il minimo antenato comune cercato.
Il punteggio ottenuto nell’esame di Informatica: Programmazione dipenderà anche da quanto efficacemente saranno applicati i principi di Ingegneria
del Software visti nel corrispondente corso.
3