Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


STRUTTURE DATI E ALGORITMI, Dispense di Algoritmi E Strutture Di Dati

Questa dispensa è divisa in 2 parti: - una parte teorica che descrive gli algoritmi e dimostrazioni delle varie strutture dati. - una parte scritta in cui vengono svolti esercizi d'esame. Sono presenti 10 tipi di compiti differenti.

Tipologia: Dispense

2019/2020

In vendita dal 26/10/2021

alberto-varini
alberto-varini 🇮🇹

4

(1)

5 documenti

1 / 90

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
1
DATI
ALGORITMI
Dispensa del corso Dati algoritmi di ingegneria informatica
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40
pf41
pf42
pf43
pf44
pf45
pf46
pf47
pf48
pf49
pf4a
pf4b
pf4c
pf4d
pf4e
pf4f
pf50
pf51
pf52
pf53
pf54
pf55
pf56
pf57
pf58
pf59
pf5a

Anteprima parziale del testo

Scarica STRUTTURE DATI E ALGORITMI e più Dispense in PDF di Algoritmi E Strutture Di Dati solo su Docsity!

DATI

ALGORITMI

Dispensa del corso Dati algoritmi di ingegneria informatica

Sommario

Alberi

Profondità di un nodo

public int depth(Tree t, Position p) {

If(t.isRoot())

return 0;

return 1+ depth(t,parent(p))

Definizione : la profondità è il numero di antenati di p, diversi dalla posizione stessa.

Prestazioni : 𝑂(𝑑𝑣) dove 𝑑𝑣 è la profondità dell’albero.

Caso peggiore : 𝑂

perché 𝑑𝑣 può essere al massimo 𝑛 − 1.

Altezza di un nodo

public int heigth(t, Position p){

Int h=0;

If(t.isExternal(p))

return h;

for(Position c : children(p))

h=Math.max(h, 1+ heigth(c));

return h

Definizione : l’altezza è il valore massimo delle profondità delle sue posizioni.

Prestazioni : 𝑂(𝑔𝑣) dove 𝑔𝑣 è il numero di figli di ciascuna posizione.

Caso peggiore : 𝑂

perché 𝑔𝑣 può essere al massimo 𝑛 − 1.

Attraversamenti in pre-Ordine, post-order,

ampiezza

public void preOrder(Position p){

visit(p);

for(Position c : children(p)

preOrder(c);

public void postOrder(Position p){

for(Position c : children(p)

postOrder(c);

visit(p);

public void breadth (Position p){

Queue q=new Queue();

q.enqueue(p); //Si suppone che p sia la radice dell’albero

while(!q.isEmpty()){

Position p= q.dequeue();

visit(p);

for(Position c : children(p))

q.enqueue(c);

Prestazioni : Tutti e 3 gli algoritmi sono 𝑂

Contare le foglie di un albero (viene restituito un valore che rappresenta un risultato

globale, elaborato dall’attraversamento dell’albero)

public static int countLeaves(Tree t){

if (t == null || t.isEmpty()) return 0;

return countLeaves(t, t.root());

private static int countLeaves(Tree t, Position v){

if (t.isExternal(v)) // azione di visita di una foglia

return 1;

int count = 0; // azione di visita di un nodo interno

for (Position x : T.children(v))

count += countLeaves(t, x);

return count; // foglie nel sottoalbero con radice v

Contare i nodi interni di un albero

public static int countLeaves(Tree t){

if (t == null || t.isEmpty()) return 0;

return countLeaves(t, t.root());

private static int countLeaves(Tree t, Position v){

if (t.isExternal(v)) // azione di visita di una foglia

return 0 ;

int count = 1 ; // azione di visita di un nodo interno

for (Position x : T.children(v))

count += countLeaves(t, x);

return count; // foglie nel sottoalbero con radice v

Prestazioni : entrambi gli algoritmi sono 𝑂

perché l’operazione di visita è 𝑂

Applicazione di attraversamento pre-order per l’implementazione del metodo

positions() dell’interfaccia Tree.

public Iterable<Position> positions(){

ArrayList<Position> list= new ArrayList<Position>(); // vuota

if (isEmpty())

return list; // lista vuota

preorderAppendPosition(list, root());

list.trimToSize(); // ritaglia la capacità per evitare spreco di spazio

return list;

private void preorderAppendPosition(ArrayList<Position> list, Position v){

list.add(v); // azione di visita 𝜃( 1 ) in media

for (Position c : children(v))

preorderAppendPosition(list, c);

Prestazioni : l’algoritmo positions ha prestazioni 𝑂(𝑛) perché l’operazione di visita è

Applicazioni attraversamento postOrder

Assegnare l’altezza a ciascun nodo

public static void setHeigth(Tree t){

If(t != null && !t.isEmpty())

setHeigth(t, t.root());

private static void setHeigth(Tree t, Position v){

Int height=0; //Se è una foglia

for(Position c : t.children(v)){

setHeigth(t, c);

int h= c.element(); //Altezza salvata nel nodo

if(h+1 > height)

height=h+1;

Caso base : ℎ = 0 la tesi si riduce 𝑛

𝐸

0

Se l'albero ha altezza zero, allora la sua radice ha altezza zero e di conseguenza è una

foglia. Se la radice di un albero è una foglia allora non ha discendenti propri e quindi

la radice è l’unico nodo esterno.

Caso Per ∀ ℎ > 0

Usando le ipotesi induttive sui due sottoalberi della radice, si possono definire:

𝐿

sottoalbero sinistro, 𝑆

𝑅

sottoalbero destro e deve valere 𝑆

𝐿

𝑅

𝐿

𝑅

insiemi che contengono i nodi esterni dei sottoalberi 𝑆

𝐿

𝑅

della radice

r.

𝐸𝐿

𝐸𝑅

cardinalità degli insiemi 𝐸

𝐿

𝑅

(cioè numero di nodi esterni dei 2

sottoalberi.

𝐿

𝑅

L'insieme dei nodi esterni dell’albero è l'unione degli insiemi 𝐸

𝐿

𝑅

dei sottoalberi,

quindi 𝑛

𝐸

𝐸𝐿

𝐸𝑅.

Dunque, per ∀ ℎ > 0 e se ∃ 𝑆

𝐿

𝑅

(esistono entrambi):

𝐸

𝐸𝐿

𝐸𝑅

𝐻𝐿

𝐻𝑅

ℎ− 1

ℎ− 1

ℎ− 1

𝒉

. (limite superiore)

𝐸

𝐸𝐿

𝐸𝑅

≥ 2 perché 𝑛

𝐸𝐿

≥ 1 e 𝑛

𝐸𝑅

≥ 1 ma se uno dei 2 sottoalberi non

esiste bisogna considerare 𝑛

𝐸

≥ 𝟏. (limite inferiore)

Dimostrazione 2 proprietà

Usando il principio di induzione applicato all’altezza ℎ dell’albero, dimostrare che in

ogni albero binario vale la relazione ℎ ≤ 𝑛

𝐼

− 1 , dove 𝑛

𝐼

è il numero di nodi

interni dell’albero.

Caso base : ℎ = 0 la tesi si riduce 𝑛

𝐼

0

Se l'albero ha altezza zero, allora la sua radice ha altezza zero e di conseguenza è una

foglia. Se la radice di un albero è una foglia allora non ha discendenti propri e quindi

non ha nodi interni.

Caso Per ∀ ℎ > 0

Usando le ipotesi induttive sui due sottoalberi della radice, si possono definire:

𝐿

sottoalbero sinistro, 𝑆

𝑅

sottoalbero destro e deve valere 𝑆

𝐿

𝑅

𝐿

𝑅

insiemi che contengono i nodi interni dei sottoalberi 𝑆

𝐿

𝑅

della radice r.

𝐼𝐿

e 𝑛

𝐼𝑅

cardinalità degli insiemi 𝐼

𝐿

𝑅

(cioè numero di nodi interni dei 2

sottoalberi.

𝐿

𝑅

L'insieme dei nodi interni dell’albero è l'unione degli insiemi 𝐼

𝐿

𝑅

dei sottoalberi 𝑆

𝐿

quindi 𝑛 𝐼

𝐼𝐿

𝐼𝑅

Dunque, per ∀ ℎ > 0 e se ∃ 𝑆

𝐿

𝑅

(esistono entrambi):

𝐼

𝐼𝐿

𝐼𝑅

𝐻𝐿

𝐻𝑅

ℎ− 1

ℎ− 1

ℎ− 1

𝒉

− 𝟏 (limite superiore)

Se esiste un solo sottoalbero:

𝐼

𝐼𝐿

𝐼𝑅

𝐿

  • 1 = 𝒉 (limite inferiore) perché 𝑛

𝐼𝐿

𝐿

e 𝑛

𝐼𝑅

Dimostrazione 3 proprietà

Sommando membro a membro le prime relazioni dimostrate precedentemente:

𝐸

𝐼

Si ottiene:

𝐸

𝐼

𝒉+𝟏

Dimostrazione 4

Avendo dimostrato la terza proprietà:

ℎ+ 1

Si può agevolmente dimostrare la quarta:

ℎ+ 1

ℎ+ 1

2

𝟐

Dimostrazione proprietà 2 (caso albero binario proprio)

Usando il principio di induzione applicato all’altezza ℎ dell’albero, dimostrare che in

ogni albero binario proprio vale la relazione ℎ ≤ 𝑛 𝐼

− 1 , dove 𝑛

𝐼

è il numero

di nodi interni dell’albero.

Caso base : ℎ = 0 la tesi si riduce 𝑛

𝐼

0

Se l'albero ha altezza zero, allora la sua radice ha altezza zero e di conseguenza è una

foglia. Se la radice di un albero è una foglia allora non ha discendenti propri e quindi

non ha nodi interni.

Caso Per ∀ ℎ > 0

Usando le ipotesi induttive sui due sottoalberi della radice, si possono definire:

𝐿

sottoalbero sinistro, 𝑆

𝑅

sottoalbero destro e deve valere 𝑆

𝐿

𝑅

𝐿

𝑅

insiemi che contengono i nodi interni dei sottoalberi 𝑆

𝐿

𝑅

della radice r.

𝐼𝐿

e 𝑛

𝐼𝑅

cardinalità degli insiemi 𝐼

𝐿

𝑅

(cioè numero di nodi interni dei 2

sottoalberi.

𝐿

𝑅

L'insieme dei nodi interni dell’albero è l'unione degli insiemi 𝐼

𝐿

𝑅

dei sottoalberi 𝑆

𝐿

quindi 𝑛 𝐼

𝐼𝐿

𝐼𝑅

Dunque, per ∀ ℎ > 0 e se ∃ 𝑆

𝐿

𝑅

(esistono entrambi):

𝐼

𝐼𝐿

𝐼𝑅

𝐻𝐿

𝐻𝑅

ℎ− 1

ℎ− 1

ℎ− 1

𝒉

− 𝟏 (limite superiore)

𝐼

𝐼𝐿

𝐼𝑅

𝐿

𝑅

  • 1 ≥ max

𝐿

𝑅

  • 1 ≥ 𝒉 (limite inferiore)

perché ℎ

𝐿

𝑅

𝐿

𝑅

≥ max(ℎ

𝐿

𝑅

Dimostrazione 3 proprietà (caso albero binario proprio)

Sommando membro a membro le prime relazioni dimostrate precedentemente

𝐸

𝐼

Si ottiene:

𝐸

𝐼

𝒉+𝟏

Dimostrazione 4 (caso albero binario proprio)

Avendo dimostrato la terza proprietà:

ℎ+ 1

Si può agevolmente dimostrare la quarta:

𝒏−𝟏

𝟐

ℎ+ 1

ℎ+ 1

2

𝟐

Code prioritarie con heap

Albero binario completo

Un albero binario 𝑇 di altezza ℎ > 0 si dice completo se:

  • Tutti i livelli di profondità minori di ℎ hanno il massimo numero di nodi

possibile. (quindi il livello 𝑖 ha 2

𝑖

nodi, con 𝑖 < ℎ)

  • Al livello ℎ – 1 tutti i nodi interni si trovano a sinistra di tutti gli eventuali nodi

esterni.

  • Al livello ℎ – 1 soltanto il nodo interno che si trova più a destra (tra i nodi

interni) può avere un solo figlio, che deve essere il suo figlio sinistro.

  • L’ultimo nodo di un albero binario completo di altezza ℎ è quel nodo di livello

ℎ che ha alla propria sinistra tutti gli altri nodi di livello ℎ.

2

Dalla definizione discende che un albero binario completo può essere triangolare

oppure triangolare fino al livello 𝒉 – 𝟏 con un ultimo livello “compatto a sinistra”.

Algoritmo down-heap-bubbling

Algoritmo in pseudocodice

while (!T.isExternal(z) && (key(z) > key(T.left(z)) || key(z) > key(T.right(z)) ){

//scambia e(z) con e(parent(z))

if(key(T.left(z))<=key(T.right)){

// si scambiano i dati, non i nodi

T.replace(z, T.element(T.left(z));

z = T.left(z) // scendo a sinistra

}else{

// si scambiano i dati, non i nodi

T.replace(z, T.element(T.right(z));

z = T.right(z) // scendo a destra

Come implementare heap

Con la rappresentazione di un heap basata su array si evitano alcune complicazioni

che derivano dall’albero con struttura concatenata. In particolare, i metodi insert ,

removeMin devono individuare l’ultima posizione nel heap:

  • Nell’array è possibile accederci un tempo 𝜃( 1 ) perché è la posizione con indice
  • Nella lista concatenata si può dimostrare che ci si accede in un tempo

2

Mappe con albero binario di ricerca

Un albero binario di ricerca (Binary Search Tree, BST) è un albero binario proprio, non

vuoto, con queste ulteriori caratteristiche:

  • Ogni 𝑛

𝑖

contiene un elemento, x(v) , appartenente a un insieme totalmente

ordinato.

• I 𝑛

𝑒

non contengono alcun elemento (sono nodi “segnaposto”, placeholder, in

Java, contengono null).

  • Essendo un albero proprio vale: 𝑛

𝑒

𝑖

  • L’attraversamento in ordine simmetrico visita i nodi interni di un BST in ordine

di contenuto crescente.

Il sottoalbero sinistro della radice contiene elementi minori della radice, nel

sottoalbero destro sono maggiori.

E’ usato per memorizzare una mappa ordinata.

Algoritmo di ricerca in una mappa ordinata

SearchInBSTsubTree(T, k, v)

if T.isExternal (v)

return null // non trovato

(else) if k == key(v) // usare equals

return value(v) // trovato

(else) if k < key(v) // usare compareTo

return SearchInBSTsubTree(T, k, T.left(v))

(else) // k > key(v)

return SearchInBSTsubTree(T, k, T.right(v))

Albero AVL: albero binario di ricerca

Proprietà di bilanciamento in altezza

Per ogni posizione 𝑝 di 𝑇, le altezze dei figli di 𝑝 devono differire di al massimo

un’unità.

Proposizione sull’altezza di un albero AVL

L’altezza h di un albero AVL di dimensione n è O(log

2

n

Dimostrazione

Invece di trovare un limite superiore per l’altezza si rivela più semplice trovare il limite

inferiore quando il numero di nodi interni indicato con 𝑛(ℎ) è minimo.

Si può osservare che:

  • Per ℎ = 1 --> 𝑛
  • Per ℎ = 2 --> 𝑛( 2 ) = 2

Con ℎ ≥ 3 per la proprietà di bilanciamento dell’altezza si ha che l’albero AVL è

costituito da un sottoalbero con altezza ℎ − 1 e uno con ℎ − 2.

Dunque, è opportuno definire:

  • 𝑛(ℎ − 1 ): numero di nodi interni del sottoalbero con ℎ − 1.
  • 𝑛(ℎ − 2 ): numero di nodi interni del sottoalbero con ℎ − 2.

Si ottiene:

Attraverso le proprietà delle progressioni di Fibonacci si può notare che 𝑛(ℎ) è una

funzione crescente di conseguenza vale:

--> n

h

i

∗ n

h − 2i

con h − 2i ≥ 1

Per comodità scegliamo h − 2i = 2 quindi i =

2

− 1 e sostituendo nella formula si

ottiene:

n

h

> [ 2

2

− 1

∗ n

] > [ 2

2

− 1

∗ n

] ≥ 2

2

− 1

--> n

h

2

− 1

Elevando il logaritmo entrambi i membri:

2

2

2

𝟐

L’ultima disuguaglianza implica che un albero AVL di dimensione n ha un’altezza

inferiore a 2 ∗ log

2

(n

h

Inserimento in un albero AVL

L’inserimento di un nuovo nodo in un albero AVL esegue lo stesso algoritmo visto per

la mappa realizzata con BST “normale”. Per effetto dell’inserimento in 𝑇, una foglia,

𝑤, diventa nodo interno e un sottoinsieme del percorso minimo che collega 𝑤 alla

radice aumenta la propria altezza di un’unità: tale sottoinsieme comprende 𝑤 e un

certo numero di suoi antenati disposti consecutivamente lungo il percorso.

L’aumento delle altezze sbilancia gli antenati di 𝑤, dunque è opportuno ribilanciare

l’albero per garantire le prestazioni 𝑂(log(𝑛)). Possiamo definire come nodo 𝑧 il

primo che si è sbilanciato risalendo verso la radice. Il sottoalbero avente radice in 𝑧 è

minimamente sbilanciato perché i 2 sottoalberi hanno altezze che differiscono di 2. A

questo punto possiamo definire:

  • 𝑦 figlio di 𝑧 con altezza maggiore.
  • 𝑥 figlio di 𝑦 con altezza maggiore.

A questo punto possiamo ripristinare il bilanciamento del sottoalbero invocando il

metodo di ristrutturazione di una terna di nodi. Che consiste nel prendere il nodo

con chiave intermedia tra i tre: 𝑧, 𝑦, 𝑥 e lo si fa diventare radice. Gli altri 2 nodi si

sistemano di conseguenza come figli sinistro e destro e i sottoalberi si “attaccano” nei

rami liberi rispettando l’ordinamento precedente.

Le prestazioni della ristrutturazione sono 𝜃( 1 ) e hanno un effetto globale. Nel

complesso l’algoritmo di inserimento è 𝑂(log(𝑛)) perché l’altezza dell’albero AVL è

𝑂(log

) grazie al bilanciamento della sua altezza.