Præsentation er lastning. Vent venligst

Præsentation er lastning. Vent venligst

1 Implementering af fundamentale datastrukturer. 2 Plan Stakke og køer Array-repræsentation Liste-repræsentation Hægtede lister Træer Terminologi Traversering.

Lignende præsentationer


Præsentationer af emnet: "1 Implementering af fundamentale datastrukturer. 2 Plan Stakke og køer Array-repræsentation Liste-repræsentation Hægtede lister Træer Terminologi Traversering."— Præsentationens transcript:

1 1 Implementering af fundamentale datastrukturer

2 2 Plan Stakke og køer Array-repræsentation Liste-repræsentation Hægtede lister Træer Terminologi Traversering ArrayList

3 3 En stak (LIFO = LastInFirstOut) Stak poppush En stak er en sekvens af dataelementer af samme type, som muliggør følgende to operationer: push(x): Læg dataelementet x øverst på stakken (dvs. i begyndelsen af stakken) pop: Fjern det øverste element på stakken Kun stakkens øverste element ( top ) er tilgængeligt

4 4 En stak er en abstrakt datatype public interface Stack { void push(Object x); void pop(); Object top(); Object topAndPop(); boolean isEmpty(); void makeEmpty(); } package weiss.nonstandard;

5 5 Implementering af en stak ved brug af et array 0 1 2 3 4 5 6 7 8 9 topOfStack = 2 Object -instanser theArray

6 6 Implementering uden fejlhåndtering public class ArrayStack implements Stack { private Object[] theArray; private int topOfStack; private static final int DEFAULT_CAPACITY = 10; public ArrayStack() { theArray = new Object[DEFAULT_CAPACITY]; topOfStack = -1; } public void push(Object x) { theArray[++topOfStack] = x; } public void pop() { topOfStack--; } public Object top() { return theArray[topOfStack]; } public Object topAndPop() { return theArray[topOfStack--]; } public boolean isEmpty() { return topOfStack == -1; } void makeEmpty() { topOfStack = -1; } }

7 7 Fejlhåndtering if (isEmpty()) throw new EmptyStackException(); Metoderne pop, top og topAndPop skal reagere fornuftigt, hvis de bliver kaldt, når stakken er tom. Vi vælger at lade dem kaste undtagelsen EmptyStackException i dette tilfælde. I begyndelsen af hver af de tre metoder indsættes

8 8 Array-udvidelse Hvis arrayet er fyldt, når push kaldes, fordobles dets størrelse. public void push(Object x) { if (topOfStack + 1 == theArray.length) doubleArray(); theArray[++topOfStack] = x; } private void doubleArray() { Object[] newArray = new Object[theArray.length * 2]; for (int i = 0; i < theArray.length; i++) newArray[i] = theArray[i]; theArray = newArray; } Alternativt kunne kopieringen ske således: System.arraycopy(theArray, 0, newArray, 0, theArray.length);

9 9 Amortiseret køretid Fordobling af arrayet er kostbar i tid: O(N). Fordobling er imidlertid en relativt sjældent forekommende foreteelse. En fordobling, der involverer N elementer, må nemlig være forårsaget af mindst N/2 kald af push. Vi kan fordele omkostningen O(N) for fordoblingen på hvert af de N/2 push -operationer. Derved bliver omkostningen for hvert push kun multipliceret med en konstant faktor. I det “lange løb” er køretiden for push O(1). Amortisering: regelmæssig tilbagebetaling af lån.

10 10 Implementering af en stak ved brug af en hægtet liste topOfStack class ListNode { ListNode(Object e, ListNode n) { element = e; next = n; } Object element; ListNode next; } ListNode

11 11 Implementering uden fejlhåndtering public class ListStack implements Stack { private ListNode topOfStack; public ListStack() { makeEmpty(); } public void push(Object x) { topOfStack = new ListNode(x, topOfStack); } public void pop() { topOfStack = topOfStack.next; } public Object top() { return topOfStack.element; } public Object topAndPop() { Object t = top(); pop(); return t; } public boolean isEmpty() { return topOfStack == null; } void makeEmpty() { topOfStack = null; } }

12 12 En kø (FIFO = FirstInFirstOut) En kø er en sekvens af dataelementer af samme type, som muliggør følgende to operationer: enqueue(x): Sæt dataelementet x bagest i køen dequeue: Fjern det første element fra køen dequeue enqueue Kø

13 13 En kø er en abstrakt datatype public interface Queue { void enqueue(Object x); Object dequeue(); Object getFront(); boolean isEmpty(); void makeEmpty(); } package weiss.nonstandard;

14 14 Implementering af en kø ved brug af en hægtet liste class ListNode { ListNode(Object e, ListNode n) { element = e; next = n; } Object element; ListNode next; } frontback ListNode

15 15 Implementering uden fejlhåndtering public class ListQueue implements Queue { private ListNode front, back; public ListQueue() { makeEmpty(); } public void enqueue(Object x) { if (isEmpty()) front = back = new ListNode(x, null); else back = back.next = new ListNode(x, null); } public Object dequeue() { Object t = front.element; front = front.next; return t; } public Object getFront() { return front.element; } public boolean isEmpty() { return front == null; } public void makeEmpty() { front = back = null; } }

16 16 Implementering af en kø ved brug af et array Undgå flytning af arrayelementer ved enqueue og dequeue. back = 5 01234567 front = 0

17 17 back = 5 01234567 front = 2currentSize = 4 Cirkulær buffer dequeue : back = 5 01234567 front = 3currentSize = 3

18 18 enqueue : back = 6 01234567 front = 3currentSize = 4 enqueue med “wraparound” back = 0 01234567 front = 3currentSize = 6

19 19 Implementering uden fejlhåndtering public class ArrayQueue implements Queue { private Object[] theArray; private int front, back, currentSize; private static final int DEFAULT_CAPACITY = 10; public ArrayQueue() { theArray = new Object[DEFAULT_CAPACITY]; makeEmpty(); } public void makeEmpty() { back = -1; front = 0; currentSize = 0; } public void enqueue(Object x) { back = (back + 1) % theArray.length; theArray[back] = x; currentSize++; } public Object dequeue() { Object t = theArray[front]; front = (front + 1) % theArray.length; currentSize--; return t; } public Object getFront() { return theArray[front]; } public boolean isEmpty() { return currentSize == 0; } }

20 20 Array-udvidelse Hvis arrayet er fyldt, når equeue kaldes, fordobles dets størrelse. public void equeue(Object x) { if (currentSize == theArray.length) doubleQueue(); back = (back + 1) % theArray.length; currentSize++; theArray[back] = x; } private void doubleQueue() { Object[] newArray = new Object[theArray.length * 2]; for (int i = 0; i < currentSize; i++, front = (front + 1) % theArray.length) newArray[i] = theArray[front]; theArray = newArray; front = 0; back = currentSize - 1; }

21 21 En hægtet liste class ListNode { ListNode(Object e, ListNode n) { element = e; next = n; } Object element; ListNode next; } frontOfList ListNode

22 22 Indsættelse og sletning current.next = new ListNode(x, current.next); Indsættelse af nyt element x efter current : Sletning af elementet efter current : current.next = current.next.next; current Indsættelse af nyt element forrest i listen? Sletning af det forreste element?

23 23 Brug af en header-knude forenkler indsættelse og sletning forrest i listen public class LinkedList implements List { public LinkedList() { header = new ListNode(null, null); } public boolean isEmpty() { header.next == null; } public void makeEmpty() { header.next = null; } private ListNode header; } header

24 24 Træer

25 25 Et træ er en samling af knuder og kanter, (V, E), som opfylder visse krav: En knude, v, er et simpelt dataobjekt, der kan have et navn og en tilknyttet information. En af knuderne er udpeget som rod i træet. En kant, (v 1,v 2 ), er en forbindelse imellem to knuder, v 1 og v 2. En vej er en liste af knuder, (v 1,v 2,...,v k ), hvor alle successive knuder, v i og v i+1, er indbyrdes forbundne (dvs. tilhører E). For at udgøre et træ skal der mellem roden og enhver anden knude findes præcis én vej. Træer

26 26 Terminologi Rod: R X er far til Y Y er søn til X (Y er barn af X) U, V og W er børn af T S er bedstefar til Z S er forgænger til Y (S er over Y) Y er efterkommer af S (Y er under S) Terminale knuder (blade): Y, Z, U, V, W Ikke-terminale knuder: R, S, X, T Rod R S X Y Z T U VW Terminal (blad) Ikke-terminal Niveau 0 Niveau 1 Niveau 2 Niveau 3

27 27 Terminologi (fortsat) Et træ kaldes ordnet, hvis rækkefølgen af sønnerne for enhver knude er specificeret. En knudes niveau er antallet af knuder på vejen fra knuden til roden (minus knuden selv). Et træs højde er det maksimale niveau for alle knuder i træet.

28 28 Binære træer Rekursiv definition: Et binært træ er enten et tomt træ, eller en knude, som har et venstre og et højre binært undertræ. Et binært træ er et ordnet træ, hvor hver knude har højst 2 sønner.

29 29 Klassen BinaryNode class BinaryNode { BinaryNode left, right; Object element; BinaryNode(Object e, BinaryNode lt, BinaryNode rt) { element = e; left = lt; right = rt; } }

30 30 Klassen BinaryTree public class BinaryTree { private BinaryNode root; public BinaryTree() { root = null; } public int size() { return size(root); }public int height() { return height(root); } public void preOrderPrint() { preOrderPrint(root); }public void inOrderPrint() { inOrderPrint(root); }public void postOrderPrint() { postOrderPrint(root); } private int size(BinaryNode t) {...}private int height(BinaryNode t) {...} private void preOrderPrint(BinaryNode t) {...}private void inOrderPrint(BinaryNode t) {...}private void postOrderPrint(BinaryNode t) {...} }

31 31 Brug af rekursion int size(BinaryNode t) { return t == null ? 0 : 1 + size(t.left) + size(t.right); } int height(BinaryNode t) { return t == null ? -1 : 1 + Math.max(height(t.left), height(t.right)); }

32 32 (1) Preorder: Besøg roden. Besøg venstre undertræ. Besøg højre undertræ. Traversering af binære træer (besøg alle knuder) P M S A B L G R E F T (2) Inorder: Besøg venstre undertræ. Besøg roden. Besøg højre undertræ. (3) Postorder: Besøg venstre undertræ. Besøg højre undertræ. Besøg roden. PMSABLGRTEF ASBMPLGTRFE ABSMTFERGLP

33 33 Inorder: void inOrderPrint(BinaryNode t) { if (t != null) { inOrderPrint(t.left); System.out.println(t.element); inOrderPrint(t.right); } Preorder: void preOrderPrint(BinaryNode t) { if (t != null) { System.out.println(t.element); preOrderPrint(t.left); preOrderPrint(t.right); } } Postorder: void postOrderPrint(BinaryNode t) { if (t != null) { postOrderPrint(t.left); postOrderPrint(t.right); System.out.println(t.element); } Traversering ved hjælp af rekursion

34 34 Traversering ved brug af iterator public abstract class TreeIterator { BinaryTree t: BinaryNode current; public TreeIterator(BinaryTree t) { this.t = t; current = null; } public abstract void first(); public abstract void advance(); public final boolean isValid() { current != null; } public final Object retrieve() { if (current == null) throw new NoSuchElementException(); return current.element; } }

35 35 Traversering ved hjælp af en stak (af ikke-færdigbehandlede knuder) Traverseringstypen er bestemt af antallet af gange en knude poppes fra stakken: 1 gang: preorder 2 gange: inorder 3 gange: postorder class StNode { StNode(BinaryNode n) { node = n; } BinaryNode node; int timesPopped; } 1 23

36 36 Klassen PostOrder public class PostOrder extends TreeIterator { protected Stack s; // Stack of StNode objects public PostOrder(BinaryTree t) { super(t); s = new ArrayStack(); s.push(new StNode(t.root)); } public void first() { s.makeEmpty(); if (t.root != null) s.push(new StNode(t.root)); try { advance(); } catch (NoSuchElementException e) {} } public void advance() {...} }

37 37 public void advance() { if (s.isEmpty()) { if (current == null) throw new NoSuchElementException(); current = null; return; } for (;;) { StNode cnode = (StNode) s.top(); switch (++cnode.timesPopped) { case 1: if (cnode.node.left != null) s.push(new StNode(cnode.node.left)); continue; case 2: if (cnode.node.right != null) s.push(new StNode(cnode.node.right)); continue; case 3: s.pop(); current = cnode.node; return; } Metoden advance

38 38 Klassen InOrder public class InOrder extends PostOrder { public InOrder(BinaryTree t) { super(t); } public void advance() throws ItemNotFound { if (s.isEmpty()) { if (current == null) throw new NoSuchElementException(); current = null; return; } for (;;) { StNode cnode = (StNode) s.top(); switch (++cnode.timesPopped) { case 1: if (cnode.node.left != null) s.push(new StNode(cnode.node.left)); continue; case 2: s.pop(); current = cnode.node; if (current.right != null) s.push(new StNode(current.right)); return; }

39 39 public class PreOrder extends TreeIterator { private Stack s; public PreOrder(BinaryTree t) { super(t); s = new ArrayStack(); s.push(t.root); } public void first() { s.makeEmpty(); if (t.root != null) s.push(t.root); try { advance(); } catch (NoSuchElementException e) {} } public void advance() {...} } Klassen PreOrder Hjælpeklassen StNode kan undværes

40 40 public void advance() { if (s.isEmpty()) { if (current == null) throw new NoSuchElementExcption(); current = null; return; } current = (BinaryNode) s.topAndPop(); if (current.right != null) s.push(current.right); if (current.left != null) s.push(current.left); } Metoden advance Bemærk, at current.right stakkes før current.left.

41 41 (4) Level-order: Besøg knuderne fra top til bund, i stigende niveauorden, startende i roden og fra venstre mod højre på hvert niveau. Niveau-traversering af træer P M S A B L G R E F T PMLSGABRTEF Niveau-traversering opnås ved i klassen PreOrder at erstatte stakken med en kø. Bemærk, at køen kan blive meget lang!Cirka N/2, hvor N er antallet af knuder i træet.

42 42 Traversering ved hjælp af korutiner En korutine er en rutine, der midlertidigt kan standse sin udførelse. I mellemtiden kan en anden korutine blive udført. En standset korutine kan senere genoptage sin udførelse. Denne form for programsekvensering kaldes for alternering. resume(b) resume(a) coroutine a coroutine b

43 43 Klassen Coroutine public class abstract Coroutine { protected abstract void body(); public static void resume(Coroutine c); public static void call(Coroutine c); public static void detach(); }

44 44 public class InOrderIterator extends TreeIterator { public InOrderIterator(BinaryTree t) { super(t); } private void traverse(BinaryNode t) { if (t != null) { traverse(t.left); current = t; detach(); traverse(t.right); } else current = null; } Rekursiv inorder-traversering ved hjælp af koroutinesekvensering

45 45 public abstract class TreeIterator extends Coroutine { BinaryTree t; BinaryNode current; public TreeIterator(BinaryTree theTree) { t = theTree; current = null; call(this); } abstract void traverse(BinaryNode t); protected void body() { traverse(t.root); } public boolean isValid() { return current != null; } public void advance() { if (current == null) throw new NoSuchElementExcption(); call(this); } public Object retrieve() { if (current == null) throw new NoSuchElementExcption(); return current.element; }

46 46 public class InOrderTest { public static void main(String[] args) { BinaryTree t = new BinarySearchTree(); t.insert(new Integer(4)); t.insert(new Integer(2)); t.insert(new Integer(6)); t.insert(new Integer(1)); t.insert(new Integer(3)); t.insert(new Integer(5)); t.insert(new Integer(7)); InOrderIterator itr = new InOrderIterator(t); for ( ; itr.isValid(); itr.advance()) System.out.print(itr.retrieve() + " "); System.out.println(); } Et testprogram Udskrift: 1 2 3 4 5 6 7

47 47 Designmønstre Designmønster: En model for samspillet imellem objekter, der ind- fanger nyttige strukturer ved løsning af ofte forekommende problemer Eksempler: Container: udgør en beholder for en mængde af objekter. Iterator: giver sekventiel tilgang til objekterne i en container, uden at den interne repræsentation afsløres for klienten. Adapter:tilpasser grænsefladen for en klasse til bestemte forventninger hos en klient. Visitor: repræsenterer en operation, der skal udføres på objekterne i en container.

48 48 Visitor-designmønsteret interface Visitor { void visit(Object obj); boolean isDone(); } class Container { void accept(Visitor v) { for each object obj in this container { if (v.isDone()) return; v.visit(obj); }

49 49 Eksempel på en visitor class PrintingVisitor implements Visitor { public void visit(Object obj) { System.out.println(obj); } public boolean isDone() { return false; }

50 50 Rekursiv dybde-først-traversering ved hjælp af en visitor interface TreeVisitor { void preVisit(Object obj); void inVisit(Object obj); void postVisit(Object obj); boolean isDone(); } public class BinaryTree { BinaryNode root;... public void depthFirstTraversal(TreeVisitor v) { if (root != null) root.depthFirstTraversal(v); }

51 51 class BinaryNode { BinaryNode left, right; Object element;... void depthFirstTraversal(TreeVisitor v) { v.preVisit(element); if (left != null) left.depthFirstTraversal(v); v.inVisit(element); if (right != null) right.depthFirstTraversal(v); v.postVisit(element); } Rekursiv dybde-først-traversering i klassen BinaryNode

52 52 abstract class TreeVisitorAdapter implements TreeVisitor { void preVisit(Object obj) {} void inVisit(Object obj) {} void postVisit(Object obj) {} boolean isDone() { return false; } } public class InOrder extends TreeVisitorAdapter { public InOrder(Visitor v) { visitor = v; } public void inVisit(Object obj) { visitor.visit(obj); } public boolean isDone() { return visitor.isDone(); } private Visitor visitor; } Visitor for inorder-traversering

53 53 BinaryTree t = new BinaryTree();... t.depthFirstTraversal(new InOrder(new PrintingVisitor())); Eksempel på anvendelse

54 54 Implementering af ArrayList

55 55 En kollektion er et objekt, der fungerer som en beholder for andre objekter. Disse kaldes for kollektionens elementer. En mængde (Set) er en uordnet kollektion, som ikke må indeholde dubletter. En liste (List) er en ordnet kollektion, som gerne må indeholde dubletter. Ethvert element har en position. En afbildning (Map) er en uordnet kollektion af nøgle-værdi-par. Nøglerne skal være unikke. Kollektioner

56 56 Map SortedMap Collection SortedSet SetList Grænseflader for kollektioner java.util.*

57 57 void add(Object o) void addAll(Collection c) void clear() boolean contains(Object o) boolean containsAll(Collection c) boolean isEmpty() Iterator iterator() void remove(Object o) void removeAll(Collection c) void retainAll(Collection c) int size() interface Collection

58 58 interface List extends Collection void add(int i, Object o) Object get(int i) int indexOf(Object o) int lastIndexOf(Object o) ListIterator listIterator() ListIterator listIterator(int i) void remove(int i) void set(int i, Object o) List subList(int i, int j) Nye metoder:

59 59 class AbstractCollection public abstract class AbstractCollection implements Collection { public boolean isEmpty() { return size() == 0; } public void clear() { Iterator itr = iterator(); while (itr.hasNext()) { itr.next(); itr.remove(); } }... } fortsættes

60 60 public Object[] toArray() { Object[] copy = new Object[size()]; Iterator itr = iterator(); int i = 0; while (itr.hasNext()) copy[i++] = itr.next(); return copy; } fortsættes public boolean contains(Object x) { if (x == null) return false; Iterator itr = iterator(); while (itr.hasNext()) if (x.equals(itr.next())) return true; return false; }

61 61 public boolean remove(Object x) { if (x == null) return false; Iterator itr = iterator(); while (itr.hasNext()) if (x.equals(itr.next())) { itr.remove(); return true; } return false; } fortsættes

62 62 public final int hashCode() { int hashVal = 1; Iterator itr = iterator(); while (itr.hasNext()) { Object obj = itr.next(); hashVal = 31 * hashVal + (obj == null ? 0 : obj.hashCode()); } return hashVal; } fortsættes

63 63 public final boolean equals(Object other) { if (other == this) return true; if (!(other instanceof Collection)) return false; Collection rhs = (Collection) other; if (size() != rhs.size()) return false; Iterator lhsItr = this.iterator(); Iterator rhsItr = rhs.iterator(); while (lhsItr.hasNext()) if (!isEqual(lhsItr.next(), rhsItr.next())) return false; return true; } private boolean isEqual(Object lhs, Object rhs) { if (lhs == null) return rhs == null; return lhs.equals(rhs); }

64 64 ArrayList En arrayliste ( ArrayList ) er en liste, der benytter et array til at opbevare sine elementer. Collection ListAbstractCollection ArrayList

65 65 public class ArrayList extends AbstractCollection implements List { public ArrayList() { clear(); } public ArrayList(Collection other) { clear(); Iterator itr = other.iterator(); while (itr.hasNext()) add(itr.next()); } public int size() { return theSize; }... private static final int DEFAULT_CAPACITY = 10; private static final int NOT_FOUND = -1; private Object[] theItems; private int theSize; private int modCount = 0; } fortsættes

66 66 public void clear() { theSize = 0; theItems = new Object[DEFAULT_CAPACITY]; modCount++; } public Object get(int idx) { if (idx = size()) throw new ArrayIndexOutOfBoundsException( "Index " + idx + "; size " + size()); return theItems[idx]; } public Object set(int idx, Object newVal ) { if (idx = size()) throw new ArrayIndexOutOfBoundsException( "Index " + idx + "; size " + size()); Object old = theItems[idx]; theItems[idx] = newVal; return old; } fortsættes

67 67 public boolean contains(Object x) { return findPos(x) != NOT_FOUND; } private int findPos(Object x){ for (int i = 0; i < size(); i++) if (x == null) { if (theItems[i] == null) return i; } else if (x.equals(theItems[i])) return i; return NOT_FOUND; } fortsættes

68 68 public boolean remove(Object x) { int pos = findPos(x); if (pos == NOT_FOUND) return false; remove(pos); return true; } public Object remove(int idx) { Object removedItem = theItems[idx]; for (int i = idx; i < size() - 1; i++) theItems[i] = theItems[i + 1]; theSize--; modCount++; return removedItem; } fortsættes

69 69 public boolean add(Object x) { if (theItems.length == size()) { Object[] old = theItems; theItems = new Object[theItems.length * 2 + 1]; for (int i = 0; i < size(); i++) theItems[i] = old[i]; } theItems[theSize++] = x; modCount++; return true; } fortsættes

70 70 public Iterator iterator() { return new ArrayListIterator(0); } public ListIterator listIterator(int idx) { return new ArrayListIterator(idx); } private class ArrayListIterator implements ListIterator { private int current; private int expectedModCount = modCount; private boolean nextCompleted = false; private boolean prevCompleted = false; ArrayListIterator(int pos) { if (pos size()) throw new IndexOutOfBoundsException(); current = pos; }... } fortsættes

71 71 public boolean hasNext() { if (expectedModCount != modCount) throw new ConcurrentModificationException(); return current < size(); } public boolean hasPrevious() { if (expectedModCount != modCount ) throw new ConcurrentModificationException(); return current > 0; } fortsættes

72 72 public Object next() { if (!hasNext()) throw new NoSuchElementException(); nextCompleted = true; prevCompleted = false; return theItems[current++]; } public Object previous() { if (!hasPrevious()) throw new NoSuchElementException( ); prevCompleted = true; nextCompleted = false; return theItems[--current]; } fortsættes

73 73 public void remove() { if (expectedModCount != modCount) throw new ConcurrentModificationException( ); if (nextCompleted) ArrayList.this.remove(--current); else if (prevCompleted) ArrayList.this.remove(current); else throw new IllegalStateException(); prevCompleted = nextCompleted = false; expectedModCount++; }

74 74 Læs kapitel 19 Afsnit 19.6 kan læses kursorisk Løs følgende opgaver Opgave 34: 15.9 (1 point) Opgave 35: 16.1 (2 point) Opgave 36: 16.5 (2 point, ikke-obligatorisk) Opgave 37: 17.3 (2 point) Opgave 38: 17.17 (2 point, ikke-obligatorisk) Opgave 39: 18.3 (1 point) Opgave 40: 18.9 (2 point, ikke-obligatorisk) Afleveringsfrist: tirsdag den 4. december Ugeseddel 10 20. november - 27. november


Download ppt "1 Implementering af fundamentale datastrukturer. 2 Plan Stakke og køer Array-repræsentation Liste-repræsentation Hægtede lister Træer Terminologi Traversering."

Lignende præsentationer


Annoncer fra Google