F 5 Representationer Läsanvisning: kap 2.5-... Hur bra är länkade listor egentligen? enkellänkade listor dubbellänkade listor rekursiva listor Nästa gång: (dvs F6+7 på tisdag): Träd Läsanvisning: kap 6 utom 6.5 som kommer senare samt 9.1 och 9.2 F5 Länkade listor 1 F5 Länkade listor 2 Fördelar med länkade listor: + Länkade listor är dynamiska, allokerar bara så mycket minne som behövs, expanderar lätt. + Det är lätt att ta bort/sätta in noder i listan när man väl hittat platsen för det + Kräver ej kontinuerligt minne (Noderna sparas inte nära varandra i minnet) Nackdelar med länkade listor: - Slösar med minne såtillvida att varje nod behöver en pekare. - Dynamisk minnesallokering är dyrbart - Listor är sekvensiell access dvs man måste läsa från början till slut. Och det tar tiiiid. - Dålig locality dvs noderna sparas inte nära varandra i minnet vilket kan ge ökad tid för access (ny del av minnet måste läsas in hela tiden) - Svårt att gå baklänges i enkellänkad lista. Dubbellänkad kräver extra minne. Previous, Retrieve, Delete O(n), Insert O(n), O(1) om vi är på rätt ställe Comparison of list data structures Linked list Dynamic array Balanced tree Indexing Θ(n) Θ(1) Θ(log n) Insert/delete at beginning Insert/delete at end Insert/delete in middle Θ(1) Θ(n) Θ(log n) Θ(n) when last element is unknown; Θ(1) when known search time + Θ(1) Θ(k) amortized Θ(log n) Θ(n) Θ(log n) Wasted space Θ(n) Θ(n) Θ(n) (average) Don't uncriticaly trust mathematical models! Computer cache, RAM and memory architecture are completely disregarded and only the mathematical complexity is regarded. http://en.wikipedia.org/wiki/linked_list F5 Länkade listor 3 F5 Länkade listor 4
Mathematical models can lead you astray! Linked lists can suffer from the lack of locality of reference and the unability to take advantage of cashing. Big-O will only tell you how performance will degrade as n increases. So comparing one a data structure that is RAM intensive to another data structure that is cache friendly from an abstract Big-O point-of-view is just pointless. This is nothing new, but since the books and online resources rarely, or ever, mention this not many know about it, or at least tend to forget. Länkade listor enbart när - du vill kunna sätta in/ta ut effektivt i mitten men inte behöver random access eller måste söka (fördel listor) - om du har ont om minne och storleken ändras mycket (nackdel dyn.fält) - delade strukturer Dubbellänkade om man måste kunna gå baklänges. Funkar en dyn. array hyfsat => använd den Donald Knuth: We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. https://kjellkod.wordpress.com/2012/02/25/why-youshould-never-ever-ever-use-linked-list-in-your-codeagain/ F5 Länkade listor 5 F5 Länkade listor 6 Vad är länkade speciellt bra till då? - Antag att du har en att göra lista som du går igenom. Uppgifterna genererar nya uppgifter som skall göras direkt så du vill lägga in dom i listan efter denna uppgiften. - Glesa strukturer tex glesa matriser - One application is to pre-allocate all objects into pools (which are linked lists) during initialization; so whenever we need a new object we can just remove the head of the list. - Lagring av polynom: sekventiella operationer, ingen random access behövs 5x 12 + 2x 9 + 4x 7 + 6x 6 +x 3 detta 12 gradiga polynom representeras enkelt med en länkad lista. Varje nod behöver spara exponent och koefficient för resp. term. Hur man kan addera polynom, tex med 2x 7 + 8x 6 + 6x 4 + 2x 2 + 40 Resulterande polynom blir 5x 12 + 2x 9 + 6x 7 + 14x 6 + 6x 4 + x 3 + 2x 2 +40 Nested classes stapelbara klasser? Klasser inuti klasser existerar inuti sina omslutande klasser should exist only to serve its enclosing class även om dom är privata så kommer omslutande klassen åt allt static classes non-static inner classes (+local classes) (+anonymous classes) - has nothing to do - som en with outer class :-) instansvariabel, skapas kan skapas separat vanligen samtidigt - har INTE tillgång - har tillgång till till variabler i variabler i omslutande omslutande klassen klassen (utom de statiska) - existerar bara inuti omslutande klassen - OuterClass.StaticClass F5 Länkade listor 7 F5 Länkade listor 8
En hjälpklass för noder private static class Node<E> { /** The data value. private E data; /** The link to the next node. private Node<E> next = null; /** Construct a node with the given data value. @param dataitem The data value private Node(E dataitem) { this( dataitem, null ); /** Construct a node with the given data and ref value. @param dataitem The data value @param noderef the node to reference private Node(E dataitem, Node<E> noderef) { data = dataitem; next = noderef; //end class Node Översikt public class SingleLinkedList<E> { private Node<E> head = null; private int size = 0; olika varianter av add/get/set... public E remove(int index) {... private static class Node<E> {... // enl. tidigare private class SingleLinkedListIterator implements Iterator<E> {... public SingleLinkedListIterator(){.. public SingleLinkedListIterator (int index) {... * public boolean hasnext( ) {... * public E next( ) {... * public void remove( ) {... private final void checkforcomodification(){... public void set(e item) {... // end SingleLinkedList F5 Länkade listor 9 F5 Länkade listor 10 addfirst Tänk alltid i termer av mitt i först, sedan randvillkor. AddFirst är lite speciell eftersom head skall ändras. Genomlöpning av listan Princip: (utan iterator) while (p!= null) gör nåt här Tex metod för utskrift av nodernas innehåll: public void addfirst(e obj) { Node<E> p = new Node<E>(); //1 p.data = obj; //2 p.next = head; //3 head = p; //4 // eller allt på en gång med //head = new Node<E>(obj, head); F5 Länkade listor 11 public void printlist() { while (p!= null) { System.out.println(p.data); ( data förutsätts ha en tostring definierad på sig och vara skild från null) F5 Länkade listor 12
Sökning Efter platsen för ett speciellt objekt: private Node<E> find(e obj) { while ( p!= null &&!p.data.equals(obj) ) { return p; //returnera pekaren till.. Efter data på ett speciellt index: public E get(int index) { if(index < 0 index >= size){ throw new IndexOutOfBoundsException( "get: index out of bounds: " + index); else {// Search from the beginning for (int i = 0; i < index; i++) { // end if return p.data; // end get Lägg till i mitten eller sist 1) Hitta platsen p (noden före) 2) Node newnode = new Node(obj, p.next); p.next = newnode; eller som one-liner p.next = new Node(obj,p.next); (Jag skippar <E> i pseudokoden from. nu) F5 Länkade listor 13 F5 Länkade listor 14 add public void add(int index, E obj) throws IndexOutOfBoundsException{ if(index < 0 index > size){ throw new IndexOutOfBoundsException( "add: index out of bounds: " + index); else { if (index == 0) { // insert first head = new Node<E>(obj, head); else { // Search from the beginning for(int i=1; i < index; i++){ // insert the new node after p.next = new Node<E>(obj, p.next); size++; modcount++; // end if // end add Första elementet: head = head.next; Ta bort element Övriga element a) Hitta elementet före b) p.next = p.next.next; F5 Länkade listor 15 F5 Länkade listor 16
remove public E remove(int index) throws IndexOutOfBoundsException { if(index < 0 index >= size) { throw new IndexOutOfBoundsException(..); // first element special case else if ( index == 0 ) { E item = head.data; head = head.next; size--; modcount++; return item; else { // another element // find the node before the // one to take away for (int i=1; i<index; i++) { E item = p.next.data; p.next = p.next.next; size--; modcount++; return item; F5 Länkade listor 17 SingleLinkedListIterator( ) även här har vi nytta av iteratorer. Om vi tex vill göra nåt med varje element i listan så känns det kanske naturligt att göra såhär: for (int i=0; i<alist.size(); i++) { E nextelement = alist.get(i); // do something with nextelement men det blir inte bra, varför inte? (Titta på hur get fungerar) Bättre: Vi håller reda på vilken nod som är nästa men också vilket index som är nästa. lastreturned är index till sist returnerade nod public SingleLinkedListIterator() { currentnod = head; currentindex = 0; lastreturned = -1; F5 Länkade listor 18 Nu kan loopen for (int i=0; i<alist.size(); i++) { E nextelement = alist.get(i); // do something with nextelement som tar O(n 2 ) istället skrivas Iterator<E> itr = alist.iterator(); while(itr.hasnext() { E nextelement = itr.next(); // do something with nextelement och den tar O(n) Iterator som startar på ett viss index public SingleLinkedListIterator( int index) { // Validate parameter. if (index < 0 index > size) { throw new IndexOutOfBoundsException( "iterator: index out of bounds: " + index); lastreturned = -1;//No item returned currentindex = index; // Special case of last item. if (index == size) { currentnod = null; else { // Start at the beginning currentnod = head; for (int i = 0; i < index; i++) { currentnod = currentnod.next; F5 Länkade listor 19 F5 Länkade listor 20
Lägg sist med extranod Lägg in ett nytt element sist i en lista med extranod. Vi har 2 fall men vi kan behandla dem lika. Dubbellänkade listor Node pre data next v private static class Node < E > { /** The data value. private E data; /** The link to the next node. private Node < E > next = null; public void addlast(e obj) { // hitta platsen // finns alltid while (p.next!= null) { p.next = new Node(obj, null)); /** The link to the previous node. private Node < E > prev = null; /** Construct a node with the given data value. @param dataitem The data value private Node(E dataitem) { data = dataitem; // ev. även konstruktorer med 2 // och 3 parametrar //end class Node F5 Länkade listor 21 F5 Länkade listor 22 Sätta in i dubbellänkad lista efter p Sätta in i dubbellänkad lista efter p public void addafter (E seekvalue, E obj) throws ListException { Node p = find(seekvalue); // hitta p Node slask = new Node(); slask.data = X; slask.next = p.next; // 1 slask.prev = p; // 2 p.next.prev= slask; // 3 ordningen p.next = slask; // 4 viktig kortare version: p.next.prev = new Node(p, X, p.next); p.next = p.next.prev; Här uppstår ofta specialfall som måste särbehandlas tex måste tom lista särbehandlas vid insättning av nytt element, sätt in sist osv. RITA FIGURER! F5 Länkade listor 23 if (p == null) { throw new ListException ("addafter: ObjectMissing"); else { Node tmp = new Node (p, obj, p.next); if ( p.next!= null ) { p.next.prev = tmp; p.next = tmp; F5 Länkade listor 24
(Enkellänkade-)Listor och rekursion en lista är antingen tom eller också består den av en nod följd av en lista lägga till sist eller i mitten public class LinkedListRec < E > { private Node<E> head;... /** Wrapper method for finding the size of a list. @return The size of the list public int size() { return size(head); 1) Hitta platsen (noden före) 2) p.next = new Node<E>(obj, p.next); private int size(node<e> p) { if (p == null) return 0; else return 1 + size(p.next); (Note: bättre att spara antalet I klassen men nu övar vi rekursion...) F5 Länkade listor 25 F5 Länkade listor 26 addlast rekursivt, enkellänkad wrapper public void addlastrek(e obj) { if (head == null) { head = new Node<E>(obj, null); else { addlastrek(head, obj); --------------------------- private void addlastrek( Node<E> head, E obj) { System.out.println( "#" + this.tostring() + "# " + "obj= " + obj); if (head.next == null) { head.next=new Node<E>(obj, null); else { addlastrek(head.next, obj); System.out.println( "##" + this.tostring() + "## " + "obj= " + obj); // end addlastrek rekursiv remove, wrapper /** Wrapper method for removing a node post: The first occurrence of outdata is removed. @param outdata The data to be removed @return true if the item is removed, and false otherwise public boolean remove(e outdata) { if (head == null) return false; else if (head.data.equals(outdata)){ head = head.next; return true; else return remove( head.next, head, outdata); head F5 Länkade listor 27 F5 Länkade listor 28
/** Removes a node from a list. @param head The head of the sublist @param pred The predecessor of the list head @param outdata The data to be removed @return true if the item is removed and false otherwise private boolean remove( Node < E > slask, Node < E > pred, E outdata) { if (slask == null) return false; else if(slask.data.equals(outdata)){ pred.next = slask.next; return true; else return remove( slask.next, slask, outdata); pred iterativ tostring public String tostring() { StringBuilder buf = new StringBuilder(); buf.append("[ "); while (p!= null) { buf.append(string.valueof( (E)p.data) ); if (p.next!= null) { buf.append(" -> "); buf.append(" ]"); return buf.tostring(); valueof för primitiva typer är i princip == tostring så String.valueOf(5) == Integer.toString(5) men är lite annorlunda för tex boolean och char[] och för objekt if the argument is null, then a string equal to "null"; otherwise, the value of obj.tostring() is returned F5 Länkade listor 29 F5 Länkade listor 30 Rekursiv tostring Wrapper: public String tostring() { if (head!= null) { return "[ " + tostringrec(head) + " ]"; else { return "[ ]"; rekursiv metod private String tostringrec( Node<E> ptr) { if (ptr.next!= null) { return String.valueOf( (E)ptr.data) + " -> " + tostringrec(ptr.next); else return String.valueOf( (E)ptr.data) + ""; Självorganiserande lista Normalt är en lista sorterad eller osorterad. Man kan ibland förbättra prestanda hos sekventiell sökning i en osorterad lista genom att använda en heuristik ( tumregel) för självorganisation i listan. Idén bygger på att ofta är 80% av accesserna till 20% av elementen. Man skall då försöka se till att element som efterfrågas ofta finns i början av listan. Tre metoder för detta: Move-to-front: När en post söks så flyttas posten till början av listan. Transpose: När en post söks så flyttas den posten ett steg närmare början av listan genom att den får byta plats med posten på platsen framför i listan. Count: När en post söks så ökas en räknare som finns i varje post i listan och sedan flyttas den posten framåt så mycket att listan är sorterad efter räknaren (vanligen bara ett steg åt gången). F5 Länkade listor 31 F5 Länkade listor 32