Præsentation er lastning. Vent venligst

Præsentation er lastning. Vent venligst

1 Implementering af fundamentale datastrukturer. 2 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 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 Stakke og køer Array-repræsentation Liste-repræsentation Hægtede lister Træer Terminologi Traversering Plan

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() throws Underflow; Object top() throws Underflow; Object topAndPop() throws Underflow; boolean isEmpty(); void makeEmpty(); } package DataStructures; import Exceptions.*;

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 StackAr implements Stack { private Object[] theArray; private int topOfStack; static final int DEFAULT_CAPACITY = 10; public StackAr() { 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 Underflow ("... "); 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 Underflow 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 såvel push som pop O(1).

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 StackLi implements Stack { private ListNode topOfStack; public StackLi() { 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() throws Underflow; Object getFront() throws Underflow; boolean isEmpty(); void makeEmpty(); } package DataStructures; import Exceptions.*;

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 QueueLi implements Queue { private ListNode front, back; public QueueLi() { 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 QueueAr implements Queue { private Object[] theArray; private int front, back, currentSize; private static final int DEFAULT_CAPACITY = 10; public QueueAr() { theArray = new Object[DEFAULT_CAPACITY]; makeEmpty(); } 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; } void makeEmpty() { back = -1; front = 0; 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

23 23 header 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; } ListNode header; }

24 24 Iteratorer En iterator er et objekt, der benyttes til at gennemløbe elementerne i en datastruktur, uden at anvenderen behøver at kende til den konkrete implementering af datastrukturen. Fordele: (1) understøtter dataabstraktion (2) sikrer mod fejl og misbrug

25 25 En iterator til gennemløb af hægtede lister public interface ListItr { void first(); boolean isInList(); void advance(); Object retrieve(); void zeroth(); void insert(Object x) throws ItemNotFound; boolean find(Object x); void remove(Object x) throws ItemNotFound; } public class LinkedListItr implements ListItr { private LinkedList theList; private ListNode current;... }

26 26 Eksempel på anvendelse af LinkedListItr static public void print(LinkedList list) { if (list.isEmpty()) System.out.print("Empty list"); LinkedListItr itr = new LinkedListItr(list); for (itr.first(); itr.isInList(); itr.advance()) System.out.print(retrieve() + " "); System.out.println(); }

27 27 Implementation af LinkedListItr public class ListItr { private LinkedList theList; private ListNode current; public ListItr(LinkedList list) { theList = list; current = theList.header.next; } public void first() { current = theList.header.next; } public boolean isInList() { return current != null && current != theList.header; } public void advance() { if current != null) current = current.next; } public Object retrieve() { return isInList() ? current.element : null; } public void zeroth() { current = theList.header; } public void insert(Object x) throws ItemNotFound {...} public boolean find(Object x) {...} public void remove(Object x) throws ItemNotFound {...} }

28 28 public void insert(Object x) throws ItemNotFound { if (current == null) throw new ItemNotFound("insertion error"); else current = current.next = new ListNode(x, current.next); } public boolean find(Object x) { ListNode n = theList.header.next; while (n != null && !n.element.equals(x)) n = n.next; if (n == null) return false; current = n; return true; }

29 29 public void remove(Object x) throws ItemNotFound { ListNode n = theList.header; while (n.next != null && !n.next.element.equals(x)) n = n.next; if (n.next == null) throw new ItemNotFound("remove fails"); n.next = n.next.next; current = theList.header; }

30 30 Træer

31 31 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

32 32 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

33 33 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.

34 34 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.

35 35 Klassen BinaryNode class BinaryNode { BinaryNode left,right; Object element; BinaryNode(Object e, BinaryNode lt, BinaryNode rt) { element = e; left = lt; right = rt; } static BinaryNode duplicate(BinaryNode t) {...} static int size(BinaryNode t) {...} static int height(BinaryNode t) {...} static void preOrderPrint(BinaryNode t) {...} static void inOrderPrint(BinaryNode t) {...} static void postOrderPrint(BinaryNode t) {...} }

36 36 Klassen BinaryTree class BinaryTree { BinaryNode root; BinaryTree() { root = null; } void duplicate(BinaryTree rhs) { if (this != rhs) root = rhs.duplicate(root); } int size() { return BinaryNode.size(root); } int height() { return BinaryNode.height(root); } void preOrderPrint() { BinaryNode.preOrderPrint(root); }void inOrderPrint() { BinaryNode.inOrderPrint(root); }void postOrderPrint() { BinaryNode.postOrderPrint(root); } }

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

38 38 (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

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

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

41 41 Traversering ved hjælp af en stak Traverseringstypen er bestemt af antallet af gange et BinaryNode - objekt “poppes” fra stakken: 3 gange: postorder 2 gange: inorder 1 gang: preorder class StNode { StNode(BinaryNode n) { node = n; } BinaryNode node; int timesPopped; }

42 42 Klassen PostOrder public class PostOrder extends TreeIterator { private Stack s; public PostOrder(BinaryTree t) { super(t); s = new StackAr(); s.push(new StNode(t.root)); } public void first() { s.makeEmpty(); if (t.root != null) s.push(new StNode(t.root)); try { advance(); } catch (ItemNotFound e) {} } public void advance() throws ItemNotFound {...} }

43 43 public void advance() throws ItemNotFound { if (s.isEmpty()) { if (current == null) throw new ItemNotFound("PostOrder advance"); current = null; return; } try { for (;;) { StNode cnode = (StNode) s.top(); switch (++cnode.timesPopped) { case 1: if (cnode.node.left != null) s.push(new StNode(cnode.node.left)); break; case 2: if (cnode.node.right != null) s.push(new StNode(cnode.node.right)); break; case 3: s.pop(); current = cnode.node; return; } } catch (Underflow e) {} } Metoden advance

44 44 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 ItemNotFound("InOrder advance"); current = null; return; } try { for (;;) { StNode cnode = (StNode) s.top(); switch (++cnode.timesPopped) { case 1: if (cnode.node.left != null) s.push(new StNode(cnode.node.left)); break; case 2: s.pop(); current = cnode.node; if (cnode.node.right != null) s.push(new StNode(cnode.node.right)); return; } } catch (Underflow e) {} }

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

46 46 public void advance() throws ItemNotFound { if (s.isEmpty()) { if (current == null) throw new ItemNotFound("PreOrder advance"); current = null; return; } try { current = s.topAndPop(); } catch (Underflow e) {} 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.

47 47 (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.

48 48 Traversering ved hjælp af korutiner En korutine er en rutine, der midlertidigt er i stand til at 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

49 49 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(); }

50 50 public class InOrderIterator extends TreeIterator { public InOrderIterator(BinaryTree t) { super(t); } 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

51 51 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() throws ItemNotFound { if (current == null) throw new ItemNotFound("TreeIterator advance"); call(this); } public Object retrieve() throws ItemNotFound { if (current == null) throw new ItemNotFound("TreeIterator retrieve"); return current.element; }

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

53 53 Designmønstre Designmønster: En model for samspillet imellem objekter, der indfanger nyttige strukturer ved løsning af en række problemer Eksempler: Container: udgør en beholder for en mængde af objekter. Iterator: giver sekventiel tilgang til objekterne i en container, uden at dennes 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.

54 54 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); }

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

56 56 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); }

57 57 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

58 58 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 { private Visitor visitor; public InOrder(Visitor v) { visitor = v; } public void inVisit(Object obj) { visitor.visit(obj); } public boolean isDone() { return visitor.isDone(); } } Visitor for inorder-traversering

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

60 60 Læs kapitel 18 i lærebogen (side 489-552) Løs følgende opgaver 8-1. Opgave 15.1 8-2. Opgave 15.3 8-3. Opgave 15.5 8-4. Opgave 16.3 8-5. Opgave 16.8 8-6. Opgave 16.18 8-7. Opgave 17.3 8-8. Opgave 17.4 8-9. Opgave 17.9 Ugeseddel 8 30. oktober - 6. november


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

Lignende præsentationer


Annoncer fra Google