Il tipo astratto di dati Node List

Transcript

Il tipo astratto di dati Node List
Il tipo astratto di dati Node List
Tipo di dati e operazioni
Tipi di dati:
oggetti arbitrari (come al solito)
Operazioni:
Metodi generici:
- integer size()
- boolean isEmpty()
Metodi di accesso:
- Position first(): Restituisce la posizione del primo elemento della lista
- Position last(): Restituisce la posizione dell’ultimo elemento della lista
- Position prev(Position p): Restituisce la posizione dell’ elemento che precede
l’elemento in posizione p
- Position next(Position p): Restituisce la posizione dell’ elemento che segue
l’elemento in posizione p
Strutture Dati
Il tipo astratto di dati Node List
Tipo di dati e operazioni
Metodi di aggiornamento:
- E set(Position p, E e): Rimpiazza l'elemento in posizione p con e, restituendo
l'elemento che prima era in posizione p
- addBefore (Position p, E e): Inserisce l’elemento e nella posizione che
precede la posizione p
- addAfter(Position p, E e): Inserisce l’elemento e nella posizione che segue la
posizione p
- addFirst(E e): Inserisce l’elemento e come primo elemento
- addLast(E e): Inserisce l'elemento e come ultimo elemento
- E remove(Position p): Rimuove e restituisce l’elemento in posizione p
Strutture Dati
Il tipo astratto di dati Node List
Eccezioni
InvalidPositionException
Viene lanciata quando viene specificata come argomento una posizione non valida
(ad esempio la posizione è null oppure è inesistente)
BoundaryViolationException
Viene lanciata quando si tenta di accedere ad una posizione al di fuori della lista
(ad esempio si chiama il metodo next sull'ultima posizione della lista)
EmptyListException
Viene lanciata quando si invocano i metodi first() o last() su una lista vuota
Strutture Dati
Il tipo astratto di dati Node List
L'interfaccia Position
package position;
public interface Position<E> {
E element();
}
Strutture Dati
Il tipo astratto di dati Node List
L'interfaccia PositionList
public interface PositionList<E> {
public int size();
public boolean isEmpty();
public Position<E> first() throws EmptyListException;
public Position<E> last() throws EmptyListException;
public Position<E> next(Position<E> p)
throws InvalidPositionException, BoundaryViolationException;
public Position<E> prev(Position<E> p)
throws InvalidPositionException, BoundaryViolationException;
public void addFirst(E e);
public void addLast(E e);
...
}
Strutture Dati
Il tipo astratto di dati Node List
L'interfaccia PositionList
public interface PositionList<E> {
...
public void addAfter(Position<E> p, E e)
throws InvalidPositionException;
public void addBefore(Position<E> p, E e)
throws InvalidPositionException;
public E remove(Position<E> p) throws InvalidPositionException;
public E set(Position<E> p, E e) throws InvalidPositionException;
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione basata su liste dopp. concatenate
header
Liste doppiam.
concatenate
NODI
trailer
ADT Lista
POSIZIONI
I nodi della lista doppiamente concatenata implementano le posizioni dell'ADT node list
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia Position
public class DNode<E> implements Position<E> {
private DNode<E> prev, next;
private E element;
public DNode(DNode<E> newPrev, DNode<E> newNext, E elem) {
prev = newPrev;
next = newNext;
element = elem;
}
public E element() {
if ((prev == null) && (next == null))
throw new InvalidPositionException("La posizione non e` in una lista!");
return element;
}
public DNode<E> getNext() { return next; }
public DNode<E> getPrev() { return prev; }
public void setNext(DNode<E> newNext) { next = newNext; }
public void setPrev(DNode<E> newPrev) { prev = newPrev; }
public void setElement(E newElement) { element = newElement; }
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
import position.*;
public class NodePositionList<E> implements PositionList<E> {
protected int numElts;
// Numero di elementi nella lista
protected DNode<E> header, trailer; // Nodi sentinella
public NodePositionList() {
numElts = 0;
header = new DNode<E>(null, null, null);
trailer = new DNode<E>(header, null, null);
header.setNext(trailer);
}
...
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
public class NodePositionList<E> implements PositionList<E> {
...
public int size() { return numElts; }
public boolean isEmpty() { return (numElts == 0); }
public Position<E> first()
throws EmptyListException {
if (isEmpty())
throw new EmptyListException("La lista e` vuota");
return header.getNext();
}
public Position<E> last()
throws EmptyListException {
if (isEmpty())
throw new EmptyListException("La lista e` vuota");
return trailer.getPrev();
}
...
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
public class NodePositionList<E> implements PositionList<E> {
...
protected DNode<E> checkPosition(Position<E> p)
throws InvalidPositionException {
if (p == null) throw new InvalidPositionException
("Null passato come posizione");
if (p == header) throw new InvalidPositionException
("header non e` una posizione valida di una lista");
if (p == trailer) throw new InvalidPositionException
("trailer non e` una posizione valida di una lista");
try {
DNode<E> temp = (DNode<E>) p;
if ((temp.getPrev() == null) || (temp.getNext() == null))
throw new InvalidPositionException
("La posizione non fa parte di una lista");
return temp;
} catch (ClassCastException e) {
throw new InvalidPositionException
("Il tipo della posizione non e` valido per questa lista");
}
}
...
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
public class NodePositionList<E> implements PositionList<E> {
...
public Position<E> prev(Position<E> p)
throws InvalidPositionException, BoundaryViolationException {
DNode<E> v = checkPosition(p);
DNode<E> prev = v.getPrev();
if (prev == header)
throw new BoundaryViolationException
("non posso retrocedere oltre l'inizio della lista");
return prev;
}
public Position<E> next(Position<E> p)
throws InvalidPositionException, BoundaryViolationException {
DNode<E> v = checkPosition(p);
DNode<E> next = v.getNext();
if (next == trailer)
throw new BoundaryViolationException
("non posso avanzare oltre la fine della lista");
return next;
}
...
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
public class NodePositionList<E> implements PositionList<E> {
...
public void addAfter(Position<E> p, E element)
throws InvalidPositionException {
DNode<E> v = checkPosition(p);
numElts++;
DNode<E> newNode = new DNode<E>(v, v.getNext(), element);
v.getNext().setPrev(newNode);
v.setNext(newNode);
}
public void addFirst(E element) {
numElts++;
DNode<E> newNode = new DNode<E>(header, header.getNext(), element);
header.getNext().setPrev(newNode);
header.setNext(newNode);
}
...
}
Strutture Dati
Il tipo astratto di dati Node List
Implementazione dell'interfaccia PositionList
public class NodePositionList<E> implements PositionList<E> {
...
public E remove(Position<E> p)
throws InvalidPositionException {
DNode<E> v = checkPosition(p);
numElts--;
DNode<E> vPrev = v.getPrev();
DNode<E> vNext = v.getNext();
vPrev.setNext(vNext);
vNext.setPrev(vPrev);
E vElem = v.element();
// scollega la posizione dalla lista rendendola invalida
v.setNext(null);
v.setPrev(null);
return vElem;
}
...
}
Strutture Dati
Esercizio
Merge sort
L'algoritmo merge sort è un algoritmo di ordinamento che ordina una
sequenza di elementi ricorsivamente nel modo seguente:
1. divide la sequenza in due sequenze non ordinate, ciascuna avente taglia
circa la metà della sequenza originale;
2. ordina separatamente ciascuna delle due sequenze;
3. fonde le due sequenze ordinate in un'unica sequenza ordinata.
Dare un'implementazione del merge sort usando l'ADT Lista.
Si assuma che gli elementi da ordinare siano di tipo Integer.
Strutture Dati
Soluzione
Merge sort
public static void mergeSort(PositionList<Integer> L){
int n = L.size();
if (n < 2)
return;
PositionList<Integer> L1 = new NodePositionList<Integer>();
PositionList<Integer> L2 = new NodePositionList<Integer>();
for (int i = 0; i < n/2; i++)
L1.addLast(L.remove(L.first()));
for (int i = n/2; i < n; i++)
L2.addLast(L.remove(L.first()));
mergeSort(L1);
mergeSort(L2);
merge(L1,L2,L);
}
Strutture Dati
Soluzione
Merge sort
public static void merge(PositionList<Integer> L1,
PositionList<Integer> L2, PositionList<Integer> L){
while (!L1.isEmpty() && !L2.isEmpty())
if (L1.first().element() <= L2.first().element())
L.addLast(L1.remove(L1.first()));
else
L.addLast(L2.remove(L2.first()));
while(!L1.isEmpty()) // sposta i rimanenti elementi di L1
L.addLast(L1.remove(L1.first()));
while(!L2.isEmpty()) // sposta i rimanenti elementi di L2
L.addLast(L2.remove(L2.first()));
}
Strutture Dati
Iteratore
A cosa serve
Una operazione molto frequente sugli ADT array list e lista di nodi è
quella di scandire la collezione di elementi un elemento alla volta
Esempi:
- cercare uno specifico elemento
- visualizzare ordinatamente gli elementi della collezione
- determinare il numero di occorrenze di un dato elemento
- sommare tutti gli elementi di una collezione
Strutture Dati
Iteratore
A cosa serve
Un iteratore è un software design pattern che permette la scansione
di un qualsiasi ADT che astrae una collezione ordinata di elementi
Un iteratore può essere visto come una estensione del concetto
di posizione (position):
- l'ADT position incapsula il concetto di “posto”
- un iteratore incapsula il concetto di “posto” e di “posto
successivo”
Strutture Dati
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
Strutture Dati
B
C
D
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
B
C
D
un iteratore è un nuovo ADT che ci permette di scandire la collezione
Strutture Dati
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
B
C
D
ADT
iteratore
A
un iteratore è un nuovo ADT che ci permette di scandire la collezione
Strutture Dati
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
B
C
D
ADT
iteratore
B
un iteratore è un nuovo ADT che ci permette di scandire la collezione
Strutture Dati
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
B
C
D
ADT
iteratore
C
un iteratore è un nuovo ADT che ci permette di scandire la collezione
Strutture Dati
Iteratore
Esempio
Data una collezione ordinata di oggetti (array list, node list)
A
B
C
D
ADT
iteratore
D
un iteratore è un nuovo ADT che ci permette di scandire la collezione
Strutture Dati
Iteratore
Definizione del tipo astratto di dati
Un iteratore può essere definito come un ADT che supporta i
seguenti due metodi:
- public boolean hasNext(): determina se ci sono altri elementi
nell'iteratore
- public E next(): restituisce l'elemento successivo in un
iteratore
Strutture Dati
Iteratore
Definizione del tipo astratto di dati
Un iteratore può essere definito come un ADT che supporta i
seguenti due metodi:
- public boolean hasNext(): determina se ci sono altri elementi
nell'iteratore
- public E next(): restituisce l'elemento successivo in un
iteratore
eccezione:
NoSuchElementException quando next() viene invocato su una
collezione vuota o sull'ultimo elemento della collezione
Strutture Dati
Iteratore
Definizione del tipo astratto di dati
Un iteratore può essere definito come un ADT che supporta i
seguenti due metodi:
- public boolean hasNext(): determina se ci sono altri elementi
nell'iteratore
- public E next(): restituisce l'elemento successivo in un
iteratore
eccezione:
NoSuchElementException quando next() viene invocato su una
collezione vuota o sull'ultimo elemento della collezione
java fornisce un iteratore con l'interfaccia java.util.Iterator
Strutture Dati
Iteratore
Implementazione
un iteratore viene solitamente associato ad uno degli ADT ad
accesso sequenziale (array list, lista di nodi)
un iteratore permette di accedere a tutti gli oggetti della
collezione nell'ordine lineare stabilito
il primo elemento di un iteratore si ottiene dalla prima chiamata
del metodo next() (assumendo che l'iteratore contenga almeno
un elemento)
un iteratore deve essere indipendente dal modo con cui è
organizzata la collezione di oggetti a cui è associato
Strutture Dati
Iteratore
Implementazione
Vogliamo un meccanismo uniforme per scandire una struttura dati ad accesso
sequenziale:
dobbiamo fare in modo che tutti i tipi astratti di dati ad accesso
sequenziale supportino il seguente metodo:
Iterator<E> iterator(): restituisce un iteratore degli elementi della
collezione
Strutture Dati
Iteratore
Implementazione
Vogliamo un meccanismo uniforme per scandire una struttura dati ad accesso
sequenziale:
dobbiamo fare in modo che tutti i tipi astratti di dati ad accesso
sequenziale supportino il seguente metodo:
Iterator<E> iterator(): restituisce un iteratore degli elementi della
collezione
in Java 5 questo metodo è fornito dall'interfaccia java.lang.Iterable
Strutture Dati
Iteratore
Implementazione
La nostra implementazione deve consentire di tener traccia del
punto della collezione indicato dal cursore dell'iteratore
creare un nuovo iteratore consiste semplicemente nel creare un
oggetto iteratore che rappresenta un cursore posto prima del primo
elemento della collezione
usando la prima volta il metodo next(), si sposta il cursore sul
primo elemento della collezione
successive invocazioni del metodo next() ci permetteranno di
scandire tutti gli elementi della collezione
il metodo hasNext() ci permette di capire quando abbiamo finito
di scandire tutta la collezione (va usato prima di invocare next())
Strutture Dati
Iteratore (lista di nodi)
Modifica dell'interfaccia PositionList<E>
Aggiungiamo il metodo iterator() all'interfaccia PositionList<E>:
public interface PositionList<E> {
...
}
Strutture Dati
Iteratore (lista di nodi)
Modifica dell'interfaccia PositionList<E>
Aggiungiamo il metodo iterator() all'interfaccia PositionList<E>:
import java.util.Iterator;
public interface PositionList<E> extends Iterable<E>{
...
public Iterator<E> iterator();
}
Nelle prossime diapositive vediamo come implementare questo metodo
Strutture Dati
Iteratore (lista di nodi)
La classe ElementIterator<E>
import java.util.Iterator;
import position.*;
public class ElementIterator<E> implements Iterator<E>{
protected PositionList<E> list; // la lista su cui implem. l'iteratore
protected Position<E> cursor; // indica la successiva posizione
public ElementIterator(PositionList<E> L) {
list = L;
cursor = (list.isEmpty())? null : list.first();
}
public boolean hasNext() { return (cursor != null);
}
public E next() throws NoSuchElementException {
if (cursor == null)
throw new NoSuchElementException("No next element");
E toReturn = cursor.element();
cursor = (cursor == list.last())? null : list.next(cursor);
return toReturn;
}
public void remove() {
}
}
Strutture Dati
Iteratore (lista di nodi)
Il metodo iterator() nella classe NodePositionList
Aggiungiamo l'implementazione del metodo iterator() (che usa la classe
ElementIterator<E>) alla classe NodePositionList<E> che implementa
l'interfaccia PositionList<E>:
public class NodePositionList<E> implements PositionList<E> {
...
public Iterator<E> iterator() {return new ElementIterator<E>(this);}
}
Strutture Dati
Merge sort
Scansione della lista con un iteratore
public static void main(String[] args) {
PositionList<Integer> L1 = new NodePositionList<Integer>();
L1.addLast(7);
L1.addLast(5);
L1.addLast(2);
L1.addLast(4);
L1.addLast(1);
L1.addLast(8);
mergeSort(L1);
Iterator<Integer> I = L1.iterator();
while (I.hasNext())
System.out.print(I.next()+" ");
}
Strutture Dati
Esercizio di test
Scrivere un metodo public static void remove (PositionList<Integer> L,
Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i.
Strutture Dati
Iteratore sulle posizioni
Per gli ADT con Position
Negli ADT che supportano la nozione di position (come la lista di
nodi) possiamo fare una scansione delle posizioni oltre che degli
elementi?
Strutture Dati
Iteratore sulle posizioni
Per gli ADT con Position
Negli ADT che supportano la nozione di position (come la lista di
nodi) possiamo fare una scansione delle posizioni oltre che degli
elementi?
Possiamo fornire anche il metodo:
positions(): restituisce un oggetto Iterable contenente le posizioni
della collezione come suoi elementi
Strutture Dati
Iteratore sulle posizioni
Per gli ADT con Position
Aggiungiamo il metodo positions() all'interfaccia PositionList<E>:
import java.util.Iterator;
public interface PositionList<E> extends Iterable<E>{
...
public Iterator<E> iterator();
public Iterable<Position<E>> positions();
}
Strutture Dati
Iteratore sulle posizioni (nodelist)
Implementazione del metodo positions()
public class NodePositionList<E> implements PositionList<E> {
...
public Iterable<Position<E>> positions() {// crea una lista di posizioni
PositionList<Position<E>> P = new NodePositionList<Position<E>>();
if (!isEmpty()) {
Position<E> p = first();
while (true) {
P.addLast(p); // aggiunge la posizione p come ultimo elemento di P
if (p == last())
break;
p = next(p);
}
}
return P; // restituisce P come oggetto Iterable
}
public Iterator<E> iterator() {return new ElementIterator<E>(this);}
}
Strutture Dati
Iteratore sulle posizioni (nodelist)
Implementazione del metodo positions()
Cosa fa il metodo positions()?
Il metodo positions() se invocato su una lista L crea una nuova
lista P contenente le posizioni di L
●
●
La lista P viene restituita come un oggetto Iterable<Position<E>>
Ciò ci permette di invocare il metodo iterator() su P per ottenere facilmente
un iteratore sulle posizioni della lista originale L
Strutture Dati
Ritorniamo al nostro esercizio
Uso del metodo positions()
Scrivere un metodo public static void remove (PositionList<Integer> L,
Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i.
Strutture Dati
Ritorniamo al nostro esercizio
Soluzione
public static void remove(PositionList<Integer> L, Integer i){
Iterable<Position<Integer>> It = L.positions();
Iterator<Position<Integer>> I = It.iterator();
while (I.hasNext()){
Position<Integer> p = I.next();
if (p.element().equals(i))
L.remove(p);
}
}
public static void main(String[] args) {
PositionList<Integer> L1 = new NodePositionList<Integer>();
...
remove(L1,1);
Iterator<Integer> I = L1.iterator();
while (I.hasNext())
System.out.print(I.next()+" ");
}
}
Strutture Dati
For-each-loop
for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){
Type name = iter.next();
loop_statement
}
for(Type name : expression){
loop_statement
}
Strutture Dati
For-each-loop
for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){
Type name = iter.next();
loop_statement
}
for(Iterator<Position<E>> iter = positions.iterator(); iter.hasNext();){
Position<E> pos = iter.next();
System.out.println(pos.element());
}
for(Type name : expression){
loop_statement
}
Strutture Dati
For-each-loop
for(Iterator<Type> iter = expression.iterator(); iter.hasNext();){
Type name = iter.next();
loop_statement
}
for(Iterator<Position<E>> iter = positions.iterator(); iter.hasNext();){
Position<E> pos = iter.next();
System.out.println(pos.element());
}
for (Position<E> pos: positions)
System.out.println(pos.element());
for(Type name : expression){
loop_statement
}
Strutture Dati
For-each-loop
Attenzione: per poter usare il for-each-loop è necessario utilizzare
le interfacce java.lang.Iterable e java.util.Iterator
Strutture Dati
Ritorniamo al nostro esercizio
Uso del metodo positions() e del for-each-loop
Scrivere un metodo public static void remove (PositionList<Integer> L,
Integer i) che rimuova da una lista L di interi tutte le occorrenze dell'intero i.
Strutture Dati
Ritorniamo al nostro esercizio
Soluzione con il for-each-loop
public static void remove(PositionList<Integer> L, Integer i){
for (Position<Integer> p : L.positions())
if (p.element().equals(i))
L.remove(p);
}
public static void main(String[] args) {
PositionList<Integer> L1 = new NodePositionList<Integer>();
...
remove(L1,1);
for (Integer i : L1)
System.out.print(i+" ");
}
Strutture Dati