Esame del corso di ALGORITMI E STRUTTURE DATI - Appello del 7/7/2004 – Compito B Tempo a disposizione: 120 minuti 1. Il tipo astratto Matrice NxN a elementi interi prevede, tra le altre, le operazioni seguenti: i) A.elemento(i, j): data la matrice A NxN e due indici i e j minori o uguali a N, restituisce il valore dell'elemento di A in posizione (i, j); ii) A.somma(B): date due matrici NxN A e B, restituisce la loro matrice somma. Implementare il tipo astratto Matrice NxN con le operazioni elemento e somma (e solo quelle). Sol.: una semplice implementazione e’ la seguente: public class MatriceNN { private static final int N = 100; private int M[][]; /* Membri privati per incapsulamento e information hiding */ /* Altre variabili di classe */ /* Costruttore */ public MatriceNN() { M = new int[N][N]; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) M[i][j] = 0; /* Possibile inizializzazione */ } /* Metodi */ public elemento(int i, int j) { return M[i][j]; } public MatriceNN somma(MatriceNN B) { MatriceNN C = new MatriceNN(); for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) C.M[i][j] = M[i][j] + B.M[i][j]; return C; } } 2. i) Si dia un esempio di input di caso migliore per l'algoritmo QuickSort su un array di 8 interi. Si assuma che il pivot sia sempre l'elemento centrale dell'array. ii) Si mostri l'evoluzione dell'algoritmo sull'input considerato. Sol.: il caso migliore per la scelta del pivot considerata si ha quando l’elemento centrale di ciascun sottoarray e’ anche il mediano, ossia l’elemento di mezzo se si considera il sottoarray in ordine crescente. Esempio: {4, 3, 2, 7, 12, 10, 8, 9} Se il pivot viene scelto in posizione n/2 in ciascun sottoarray di dimensione n, allora il primo pivot sara’ l’elemento in posizione 4 (ossia 7). Tale elemento e’ anche il mediano e rispetto ad esso l’array e’ gia’ ordinato. I due sottoarray che si ottengono al primo livello della ricorsione sono {4, 3, 2} e {12, 10, 8, 9}, aventi per pivot rispettivamente gli elementi in posizione 1 (3) e in posizione 2 (8). Si ottengono cosi’ 4 sottoarray gia’ ordinati: {2}, {4}, {9} e {10, 12}. 3. Dimostrare che l’altezza di un albero 3-ario completo e’ O(log3n), dove n e’ il numero dei nodi. Sol.: si veda la domanda n. 9 del terzo gruppo di esercizi svolti presenti sul sito. 4. Si supponga di avere a disposizione la classe IntHeap, che implementa un heap minimale a chiavi intere. La classe ha i consueti metodi void insert(int val), int deleteMin() e int getMin(), nonche' il metodo int size(), che restituisce il numero di chiavi presenti nell’heap. Si supponga poi di avere a disposizione una classe IntStack, che implementa uno stack di interi, con i metodi void push(int val), int top() e int pop(). i) Scrivere un metodo int getKMin(int k) di IntHeap che, dato un intero k, restituisce la k-esima chiave (in ordine crescente) presente nell’heap oppure size()+1 se k > size(). L'heap deve rimanere inalterato (alla fine dell'operazione). ii) Dare un limite superiore accurato alla complessita' di getKMin nel caso peggiore per heap con n elementi. Motivare la risposta. Sol.: il k-esimo elemento puo’ essere facilmente restituito eseguendo k-1 invocazioni del metodo deleteMin() e restituendo poi getMin(). L’unica complicazione e’ data dalla necessita’ di non modificare l’heap. A tale scopo si puo’ usare uno stack per ripristinare lo stato iniziale dell’heap. Una possibile implementazione e’ allora la seguente: public int getKMin(int k) { IntStack myStack = new intStack(); int val = 0; /* Conterra’ il k-esimo elemento */ for (int i = 0; i < k-1; i++) myStack.push(deleteMin()); val = getMin(); for (int i = 0; i < k-1; i++) insert(myStack.pop()); return val; } Per quanto riguarda la complessita’ si osservi che un’invocazione di deleteMin() ha costo O(log n). Infatti tale metodo restituisce la chiave minima e poi esegue l’operazione heapify per ripristinare l’heap. Considerando l’implementazione standard di heap, restituire il minimo costa O(1), mentre l’operazione di heapify ha costo O(log n) nel caso peggiore. Lo stesso si puo’ dire per ciascuna invocazione del metodo insert(): l’elemento viene inserito nella prima posizione disponibile nell’array che implementa l’heap (costo O(1)) e poi fatto risalire alla posizione giusta, operazione il cui costo e’ ancora una volta O(log n) nel caso peggiore. Poiche’ ciascun ciclo for e’ eseguito k-1 volte, il costo complessivo di un’invocazione del metodo getKMin() e’ O(k log n) nel caso peggiore. 5. Si supponga di avere le seguenti classi, che insieme implementano grafi non diretti rappresentati mediante liste di adiacenza. La lista di adiacenza di ciascun vertice e' implementata con un oggetto Vector, mentre il grafo stesso e' un array di dimensione N di oggetti di classe Vertice: public class Vertice { String etichetta; /* Es.: per assegnare identificatori agli archi */ Vector listaAdiacenza; /* Contiene etichette dei vertici adiacenti */ /* Costruttore*/ /* Metodi */ } public class Grafo { private static final int N = 100; /* No. Max. di vertici */ private Vertice[] mioGrafo; public Grafo() {/* Costruttore */ mioGrafo = new Vertice[N]; /* Altre istruzioni del costruttore */ } /* Metodi */ } /* Fine della classe Grafo */ Si scriva un metodo boolean ad(int i, int j) della classe Grafo che, dati due interi i e j, restituisca true se i vertici i-esimo e j-esimo sono adiacenti, false in caso contrario. Si ricordi che size() restituisce il No. di elementi presenti in un Vector e get(i) restituisce l'i-esimo elemento presente. Sol.: ricordando la rappresentazione di grafi mediante liste di adiacenza, mioGrafo[i] contiene l’oggetto corrispondente all’i-esimo vertice, mioGrafo[j] quello corrispondente al vertice j-esimo. I due vertici sono adiacenti qualora l’etichetta di mioGrafo[i] compaia nella lista di adiacenza di mioGrafo[j] (o viceversa, essendo il grafo non diretto). Dunque una possibile implementazione del metodo e’ la seguente: public boolean ad(int i, int j) { String e; /* Etichetta del vertice i-esimo */ if ((i > N) || (j > N)) return false; /* Errore !! */ e = mioGrafo[i].etichetta; for (int k = 0; k < mioGrafo[j].size(); k++) if (e.equals(mioGrafo[j].listaAdiacenza.get(k))) return true; return false; }