QuickSort Algoritmo di ordinamento sul posto che ha tempo di esecuzione : O(n2 ) nel caso peggiore O(n log n) nel caso medio Nonostante le cattive prestazioni nel caso peggiore, rimane il miglior algoritmo di ordinamento in media
QuickSort È basato sulla metodologia Divide et Impera: Dividi: L array A[p r] viene partizionato (tramite spostamenti di elementi) in due sotto-array non vuoti A[p q] e A[q+1 r] in cui: Impera: i due sottoarray A[p q] e A[q+1 r] vengono ordinati ricorsivamente con QuickSort Combina: i sottoarray vengono ordinati anche reciprocamente, quindi non è necessaria alcuna combinazione. A[p r] è già ordinato. ogni elemento di A[p q] è minore o uguale ad ogni elemento di A[q+1 r]
QuickSort Quick-Sort(A,p,r) IF p < r THEN q = Partiziona(A,p,r) Quick-Sort(A, p, q) Quick-Sort(A, q + 1, r)
QuickSort Procedure Partiziona: 1. l algoritmo sceglie un pivot, cioè l'elemento dell array che fungerà da elemento di confine (per il suo valore e non per la posizione) tra i due sotto-array 2. Sposta i valori maggiori del pivot verso l estremità destra dell array e i valori minori verso quella sinistra. 3. La posizione di confine q diperà dal valore scelto come pivot: tale valore è la posizione in cui si troverà alla fine della procedura l elemento più grande minore o uguale al pivot
QuickSort Partiziona(A,p,r) x = A[p] i = p - 1 j = r + 1 WHILE true DO REPEAT j = j - 1 UNTIL A[j] x REPEAT i = i + 1 UNTIL A[i] x IF i < j THEN scambia A[i] con A[j] ELSE return j
QuickSort La procedura Partiziona è quindi tale che: Se il numero di elementi dell array minori o uguali all elemento A[p], scelto come pivot, è pari a 1 (cioè A[p] è l elemento minimo) (risp. n-1) allora le dimensioni delle partizioni restituite sono 1 per la partizione di sinistra e n - 1 per quella di destra (risp n-1 e 1)
QuickSort Partiziona(A,p,r) x = A[p] i = p - 1 j = r + 1 } =Θ(1 ) WHILE true DO REPEAT j = j - 1 UNTIL A[j] x REPEAT i = i + 1 UNTIL A[i] x IF i < j THEN scambia A[i] con A[j] ELSE return j } =Θ(1 )
QuickSort Partiziona(A,p,r) x = A[p] i = p - 1 j = r + 1 } =Θ(1 ) WHILE true DO REPEAT j = j - 1 UNTIL A[j] x REPEAT i = i + 1 UNTIL A[i] x IF i < j THEN scambia A[i] con A[j] ELSE return j } =Θ(n)
QuickSort: analisi Il tempo di esecuzione di QuickSort dipe dal bilanciamento delle partizioni restituite dall algoritmo partiziona Il caso migliore si verifica quando le partizioni sono perfettamente bilanciate, entrambe di dimensione n/2 Il caso peggiore si verifica quando una partizione è sempre di dimensione 1 (la seconda è quindi di dimensione n - 1)
QuickSort: analisi Caso Migliore: le partizioni sono di uguale dimensione: T(n) = 2T(n/2) + Θ(n) e per il caso 2 del metodo principale: T(n) = Θ(n log n)
QuickSort: analisi Caso Peggiore: La partizione sinistra ha sempre dimensione 1 mentre quella sinistra ha sempre dimensione n 1 (o viceversa): T(n) = T(1) + T(n - 1) + Θ(n) poiché T(1) = 1 otteniamo T(n) = T(n - 1) + Θ(n) L equazione di ricorrenza può essere risolta facilmente col metodo iterativo n = k=1 T(n) = T(n - 1) + Θ(n) = Θ(n2 ) n Θ k = Θ k=1 k
QuickSort: analisi Caso Medio: Si può mostrare che nel caso medio il QuickSort ha tempo di esecuzione Θ(n log n), dove n è il numero di elementi da ordinare Anche quando la bi-partizione risulta molto sbilanciata nella maggior parte delle chiamate alla procedura Partiziona
QuickSort in Matlab function A = quicksort(a, p, r) % dispone in ordine crescente gli elementi del vettore x % usando l algoritmo "quicksort" if p < r [A q] = Partiziona(A, p, r); if q >= p A = quicksort(a, p, q); A = quicksort(a, q + 1, r);
QuickSort in Matlab function [x part]= partiziona(x, p, r) % esegue una partizione del vettore x. Pivot= primo elemento di x part = p-1; c = x(p); i = p; j = r; while 1 while x(i) < c i = i+1; while x(j) > c j = j-1; if i < j w = x(i); x(i) = x(j); x(j) = w;i = i+1;j = j-1; else part=j; break;
Struttura dati Dizionario Un dizionario è : un insieme di coppie (elemento, chiave) Sul campo chiave è definita una relazione d'ordine totale Operazioni: insert(elem e, chiave k) aggiunge ad S una nuova coppia (e, k) delete(elem e, chiave k) cancella da S la coppia con chiave k search(chiave k) se la chiave k è presente in s restituisce l elemento e ad esso associato altrimenti restituisce null Le operazioni di inserimento e cancellazione rono le struttura dati dinamica
Struttura dati Dizionario: Vettore non ordinato Tengo traccia del numero n di elementi effettivamente presenti nel dizionario (dimensione logica dell array) Insert: O(1) (inserisco in fondo all array) Search: O(n) (devo scorrere l array) Delete: O(n) (cerco l'elemento da cancellare e copio l ultimo elemento nella posizione cancellata)
Struttura dati Dizionario: Albero binario di ricerca Un albero binario radicato è una coppia T = (N, A) costituita da un insieme N di nodi ed un insieme A N N di coppie di nodi detti archi. In un albero: ogni nodo v (tranne la radice) ha un solo padre u tale che (u, v ) A. Nodi con lo stesso padre sono detti fratelli un nodo u può avere zero o uno o due figli (nodi v tali che (u, v) A) un nodo senza figli è detto foglia, mentre i nodi che non sono né foglie né la radice sono detti nodi interni la profondità (o livello) di un nodo è dato dal numero di archi che bisogna attraversare per raggiungerlo dalla radice altezza di un albero: massima profondità a cui si trova una foglia
Struttura dati Dizionario: Albero binario di ricerca Albero binario di ricerca : albero binario in cui ogni nodo v contiene un valore chiave(v) del dizionario un campo padre parent(v) un campo figlio sinistro sin(v) un campo figlio destro des(v) Condizione: le chiavi nel sottoalbero sin(v) sono <= chiave(v) Le chiavi nel sottoalbero des(v) sono > chiave(v) Tali proprietà inducono un ordinamento totale sulle chiavi del dizionario
Struttura dati Dizionario: Albero binario di ricerca! Albero binario di ricerca Albero binario non di ricerca
Struttura dati Dizionario: Albero binario di ricerca Visita in ordine simmetrico dato un nodo v, elenco prima il sottoalbero sinistro di v (in ordine simmetrico), poi il nodo v, poi il sotto-albero destro di v (in ordine simmetrico) algoritmo Visita_Inorder(node v) if (v null) then Visita_Inorder(sin(v)) stampa chiave(v) Visita_Inorder(des(v)) Visita_Inorder(radice del BST) visita tutti i nodi del BST una sola volta Complessità: T(n) = Θ(n). Proprietà: Visita_Inorder visita i nodi del BST in ordine crescente rispetto alla chiave
Struttura dati Dizionario: Albero binario di ricerca Search(chiave k): traccia un cammino nell albero parto dalla radice: su ogni nodo, usa la proprietà di ricerca per decidere se proseguire nel sottoalbero sinistro o destro La complessità della procedura di ricerca considerata è T(n) = O(h), dove h è l altezza del BST.
Struttura dati Dizionario: Albero binario di ricerca Insert(chiave k): inserisce nell'albero un nuovo nodo u con chiave k. Cerca la chiave k nell albero, identificando così il nodo v che diventerà padre del nodo u; tale nodo v deve essere un nodo dal quale la ricerca di k non può proseguire (e quindi deve essere un nodo v che non ha sottoalbero sinistro e/o destro) Crea un nuovo nodo u con chiave = k Appi u come figlio sinistro/destro di v in modo che sia mantenuta la proprietà di ordinamento totale La complessità della procedura considerata è T(n) = O(h), dove h è l altezza del BST
Struttura dati Dizionario: Albero binario di ricerca Delete (chiave k): cerca la chiave k nell albero, identificando così il nodo u con chiave k 1. Se u è una foglia, essa viene rimossa aggiornando il parent(u) 2. Il nodo u da eliminare ha un unico figlio f Si elimina u e si attacca f a parent(u) 3. Il nodo u da eliminare ha due figli Si individua il predecessore (risp. successore) v di u Il predecessore (risp. successore) non ha figlio destro (risp. Sinistro) Perché se avesse figlio destro(nodo maggiore del sottoalbero sinistro) non sarebbe predecessore, perché il figlio destro avrebbe chiave < k ma maggiore del predecessore Si copia v in u e si rimuove v
Struttura dati Dizionario: Albero binario di ricerca Oggetto BT in MATLAB function listobject = BT(values) btree = struct('chiave',-1,'sin',-1,'des',-1, 'parent', -1); bdim = 0; bfree = []; % array which contains the positions of btree which are free data = reshape(values,1,[]); for i = 1:numel(data) insert_bt(data(i)); listobject = struct('insert',@insert_bt,...%handle di funzione 'delete',@delete_bt,... 'search',@search_bt,... 'size',@size_bt); function size_bt disp(bdim);% Displays the data in the list
Struttura dati Dizionario: Albero binario di ricerca function insert_bt(value) [id, parent] = search_bt(value);% search the element 'value' in the binary tree if id ~=-1 error('elemento già presente'); % display message and abort else bdim = bdim+1; if numel(bfree) == 0 % se non ci sono posizioni libere id = bdim; % scelgo la prima dopo l'ultima else id = bfree(1); % scelgo la prima libera bfree = bfree(2:numel(bfree)); % e la dealloco da bfree v = struct('chiave',value,'sin',-1,'des',-1, 'parent', -1); if btree(parent).chiave <= value btree(parent).des = id; else btree(parent).sin = id; btree(id) = v; btree(id).parent = parent; % bfree
Struttura dati Dizionario: Albero binario di ricerca function [id parent] = search_bt(key) % cerca l'elemento di chiave key act = 1; prev=-1; id = 0; while 1 if act < 0 break; if key > btree(act).chiave prev = act; act = btree(act).des; else if key < btree(act).chiave prev = act; act = btree(act).sin; else id = act; break; id = act; parent = prev;
Struttura dati Dizionario: Albero binario di ricerca function delete_bt(key)... vedi file BT.m % object
Esercizi Esercizio 1. Implementare la struttura dati dizionario utilizzando un vettore invece di un albero binario di ricerca. Confrontare le due versioni considerando x inserimenti, x ricerche e x cancellazioni, con x = 20000, 40000, 50000. Esercizio 2. Con l'approccio implementativo proposto, scrivere la procedura che realizza la visita in ordine simmetrico dell'albero binario di ricerca Esercizio 3. Ripetere l'esercizio 3 della lezione 5 utilizzando un albero binario di ricerca