Esame di Algoritmi e Strutture Dati
Corso di Laurea in Ingegneria Informatica
Canali A-L, M-Z
25 settembre 2003
Cognome .......................................................................................................................
Nome ............................................................................................................................
Matricola .......................................................................................................................
Domanda 1, punti 6 Con riferimento al seguente algoritmo
public static void algo(int[] arr) { // assumere arr contiene numeri naturali distinti
if(arr.length < 1) return;
int max = arr[0];
for(int i = 1; i < arr.length; i++) if(arr[i] > max) max = arr[i];
boolean[] tmp= new boolean[max + 1];
for(int i = 0; i < tmp.length; i++) tmp[i] = false;
for(int i = 0; i < arr.length; i++) tmp[arr[i]] = true;
int j = 0;
for(int i = 0; i < tmp.length; i++)
if(tmp[i]) {
arr[j] = i;
j++;
}
}
determinare:
1. cosa fa l'algoritmo
2. la tipologia di input che determina il caso peggiore ed il relativo costo
computazionale
3. la tipologia di input che determina il caso migliore ed il relativo costo
computazionale
POSSIBILE SOLUZIONE
1. L’algoritmo ordina l’array arr in ordine crescente. Prima di tutto costruisce l’array
di booleani tmp in modo tale che l’elemento i-esimo è true se e solo se il valore i
è presente nell’array arr. Successivamente scandisce ordinatamente l’array tmp e
ricostruisce arr, inserendo in esso, nell’ordine, gli indici degli elementi true di
tmp (ossia gli elementi originariamente presenti in arr).
2. L'input, costituito dall'array arr, ha dimensione x = lg2 max ∙ n (con n =
arr.length). Il costo è in ogni caso θ(n + max + n + max) = θ(max + n). Nel
caso peggiore max  ∞ ed n non cresce: il costo diviene θ(max + n) = θ (max) =
θ (2x), cioè esponenziale nella dimensione dell'input.
3. Il caso migliore si ha quando l’array contiene una qualsiasi permutazione degli interi
0, 1, 2, …, n - 1. In tal caso il costo è θ(n) = θ(x), cioè lineare nella dimensione
dell'input.
1/5
Domanda 2, punti 6 Definire la classe BSTNode, per la rappresentazione dei nodi di un
BST, e scrivere un metodo Java che, dati un nodo v e due BST T1 e T2, verifichi se l'albero
ottenibile rendendo T1 sottoalbero sinistro di v e T2 sottoalbero destro di v è un BST.
Valutare la complessità computazionale dell'algoritmo.
POSSIBILE SOLUZIONE
public class BSTNode {
public int key;
public BSTNode leftChild, rightChild;
public BSTNode() {…}
public BSTNode(int el) {…}
public BSTNode(int el, BSTNode lt, BSTNode rt) {…}
public boolean isLeaf() {…}
}
Il metodo richiesto deve verificare se è rispettata la condizione di BST, ossia se la chiave
di ciascun elemento di T1 precede quella di v, e la chiave di v precede quella di ciascun
elemento di T2. D’altra parte, la chiave di ciascun elemento di T1 precede quella di v se e
solo se l’elemento massimo di T1 precede la chiave di v; un discorso analogo vale per T2.
boolean verifyBST(BSTNode t1, BSTNode v, BSTNode t2) {
return((t1==null)||(getMax(t1)<v.key)) && ((t2==null)||(v.key<getMin(t2));
}
int getMax(BSTNode v) {
BSTNode w=v;
while(w.rightChild!=null)
w=w.rightChild;
return w.key;
}
int getMin(BSTNode v) {
BSTNode w=v;
while(w.leftChild!=null)
w=w.leftChild;
return w.key;
}
Il costo dell’algoritmo è O(h1+h2), dove hi è l’altezza massima del BST Ti. Se n è il numero
degli elementi del BST, il costo è O(n); in particolare, quando il BST è bilanciato, il costo è
θ(log n).
2/5
Domanda 3, punti 6 Scrivere un algoritmo (Java o pseudocodice) che, dati un grafo
orientato G = (V, A) e due vertici u,v  V, determini se è vera la seguente asserzione: "u
è raggiungibile da v e v è raggiungibile da u". Valutare la complessità computazionale
dell'algoritmo.
POSSIBILE SOLUZIONE
Per verificare se un nodo w2 è raggiungibile da w1 è sufficiente effettuare una visita (per
esempio una DFS) a partire da w1: w2 è visitato se e solo se esso è raggiungibile da w1:
boolean raggiungibile(Digrapg G, Vertex w1, Vertex w2) {
for (<tutti i vertici v di G>)
marcato(v) = false;
return RaggiungibileDFS(G, w1, w2);
}
boolean raggiungibileDFS(Digraph G, Vertex v, Vertex w2) {
if(v==w2) return true;
marcato(v) = true;
for (<tutti i vertici w adiacenti a v>)
if (!marcato(w))
if(RaggiungibilitaDFS(w, w2)) return true;
return false;
}
Per risolvere l’esercizio basta verificare se u è raggiungibile da w e w è raggiungibile da u:
boolean esercizio(Digraph G, Vertex u, Vertex v) {
return raggiungibile(G, u, v) && raggiungibile(G, v, u);
}
Il costo è quello di una DFS: O(m+n), dove m è il numero degli archi di G e n è il numero
dei nodi.
3/5
Domanda 4, punti 6 Un torneo è un albero binario completo in cui ogni nodo interno
contiene la chiave massima fra quelle contenute nei suoi due figli. Il nome della struttura
dati è legato alla possibilità di rappresentare le partite di un torneo: ogni partita avviene
fra due nodi fratelli e il nodo genitore individua il vincitore.
Definire una classe Java TNode per la rappresentazione dei nodi di un torneo e scrivere un
metodo Java che, dato un torneo T, individui correttamente il secondo classificato (che
non è necessariamente chi "perde la finale"). Valutare la complessità computazionale
dell'algoritmo.
POSSIBILE SOLUZIONE
class TNode {
protected int key;
protected TNode leftChild, rightChild;
public TNode(int k) {key=k; }
public TNode(TNode lc, TNode rc) {
leftChild=lc;
rightChild=rc;
if(lc.key>rc.key) key=lc.key;
else key=rc.key;
}
}
Osserviamo che il secondo classificato, ossia l’elemento con la chiave più alta dopo il
massimo, ha sicuramente “sfidato” il primo classificato. Infatti in ogni sfida contro un
elemento, il secondo classificato risulta vincitore, a meno che lo sfidante sia proprio il
primo classificato. Pertanto, nel “calendario del torneo”, il secondo classificato in ogni sfida
sale verso la radice (cioè verso la finale), finché non incontra in partita il primo
classificato.
L’algoritmo seguente cerca il massimo tra coloro che hanno sfidato il primo classificato,
partendo dalla radice (il massimo è il vincitore della finale) e scendendo via via verso il
nodo foglia da cui il primo classificato è partito. La complessità computazionale è O(h),
dove h è l’altezza del torneo. Se il torneo è perfettamente bilanciato, la complessità è
Ө(log n), dove n è il numero di squadre partecipanti al torneo.
public int getSecond(TNode v) {
int secondo= Integer.MIN_VALUE; // come dire: secondo = -∞
while(!v.isLeaf())
if(v.rightChild.key==v.key) {
perdente=v.leftChild;
v=v.rightChild;
} else {
perdente=v.rightChild;
v=v.leftChild;
}
if(perdente.key>secondo)
secondo=perdente.key;
}
return secondo;
}
4/5
Domanda 5, punti 6 Con riferimento alla rappresentazione dei grafi non orientati
tramite:
a) liste di adiacenza
b) matrice di adiacenza
c) matrice di incidenza
determinare i costi delle seguenti operazioni:
1) test di adiacenza fra due nodi (dati due nodi, stabilire se sono adiacenti)
2) calcolo del grado di un nodo (dato un nodo, calcolare il numero di archi ad esso
incidenti)
3) test di adiacenza di archi (dati due archi, stabilire se essi sono incidenti su uno
stesso nodo)
Denotare con n il numero di nodi e con m il numero di archi del grafo.
POSSIBILE SOLUZIONE
1. Assumiamo che l'input sia una coppia di nodi v e w
a) O(g) (considero uno dei due nodi v, cerco nella lista di adiacenza di v l’altro nodo
w), dove g ≤ n è il grado del vertice
b) θ(1)
c) O(m) (considero uno dei due nodi, v; per ogni arco e, verifico se e è incidente in
v; in caso affermativo, verifico se e è incidente nell’altro nodo, w).
2. Assumiamo che l'input sia un nodo v
a) θ(g), dove g ≤ n è il grado del vertice v (conto il numero di elementi nella lista di
adiacenza di v)
b) θ(n) (per ogni nodo w, verifico se w è adiacente a v; conto il numero di nodi
adiacenti a v)
c) θ(m) (per ogni arco e, verifico se e è incidente in v; conto il numero di archi
incidenti in v)
3. a) Assumiamo che l'input sia una coppia di nodi archi (u,v) e (w,x): θ(1) (un arco è
rappresentato dai suoi estremi; possiamo verificare direttamente se due archi
hanno un estremo in comune)
b) Assumiamo che l'input sia una coppia di nodi archi (u,v) e (w,x): θ(1)
c) Assumiamo che l'input siano due archi, specificati come colonne j e k della
matrice di incidenza: O(n)
Ricapitolando:
a
b
c
1
O(g)
θ(1)
O(m)
2
θ(g)
θ(n)
θ(m)
5/5
3
θ(1)
θ(1)
O(n)