Übung Softwareentwicklung 1 für Wirtschaftsinformatik Listen Dipl.-Ing. Kerstin Altmanninger Wintersemester 2008/2009 Mittwoch, 17.12.2008 Überblick • Dynamische Datenstrukturen (allgemeine Einführung) • Listen – Verketten von Knoten – Charakteristiken von Listen – Unsortierte, einfach verkettete Liste – Sortierte, einfach verkettet Liste • Beispiele – „Stack“ als einfach verkettete Liste – „PhoneBook“ als sortierte, doppelt verkettete Liste 2/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Dynamische Datenstrukturen • Datenstrukturen, die aus verketteten Objekten (Knoten) bestehen → dynamisch – Knoten werden zur Laufzeit (also dynamisch) mittels „new“ erzeugt und dann verkettet – Die so erzeugten Datenstrukturen können (so lange Speicher vorhanden ist) dynamisch wachsen und schrumpfen • Beispiele für dynamische Datenstrukturen: – Listen – Bäume – Graphen Liste Baum Graph 3/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Verketten von Knoten (1/2) • Eine Liste besteht aus Knoten, die man miteinander verketten muss • Beispiel einer Klasse Node, wie man zwei Knoten über Zeiger miteinander verkettet: class Node { int val; //Feld stellt irgendwelche Daten dar Node next; //Feld entspricht einem Zeiger Node(int val) { //Konstruktor this.val = val; next = null; } } • Mit dem Feld, der einen Zeiger darstellt (Node next;) können NodeObjekte miteinander verkettet werden • Das Feld next zeigt entweder auf ein anderes Node-Objekt oder hat den Wert null 4/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Verketten von Knoten (2/2) a • Beispiel: 1 Node a = new Node(1); Node b = new Node(2); • • b 2 Gegeben: 2 Node-Objekte, deren Adressen in den Variablen a und b gespeichert sind Verkettung der beiden Node-Objekte: a.next = b; a b 1 2 • Die Zuweisung (b.next=null;) kann man sich sparen, da ein neu erzeugtes Node-Objekt automatisch mit null initialisiert wird • Erreichung der beiden Knoten: – Der erste Knoten kann über a erreicht werden – Der zweite Knoten kann über b oder ueber a.next erreicht werden 5/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Charakteristiken von Listen • Zur Realisierung einer Liste sind 2 Klassen notwendig: – Eine reine Datenklasse fuer die Listenknoten – Eine Klasse die die eigentliche Liste realisiert (enthält Operationen wie das Einfügen, Löschen und Suchen von Knoten) • Die Klasse, die die Liste realisiert, kann folgende Ausprägungen aufweisen: – Zeiger auf den ersten Knoten in der Liste (head) und optional einen Zeiger auf den letzten Knoten in der Liste (tail) – Unsortiert- vs. Sortiert – Einfach verkettet vs. doppelt verkettet Unsortierte, einfach verkettete Liste mit einem Zeiger auf den ersten und einen zweiten Zeiger auf den letzten Listenknoten: head tail Sortierte, doppelt verkettete Liste mit einem Zeiger auf den ersten Listenknoten: head 3 12 3 8 12 8 6/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste • Reihenfolge der Knoten entspricht nicht der Sortierreihenfolge ihrer Datenfelder Möglichkeit A • Möglichkeit B Beispiel: head head 12 • 3 8 tail 12 3 8 Klassendiagramm: List head:Node insert(x:int) append(x:int) delete(x:int) search(x:int):Node * Node val: int next: Node List head:Node tail:Node insert(x:int) append(x:int) delete(x:int) search(x:int):Node * Node val: int next: Node 7/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Einfügen von Elementen • Wenn man eine neue Zahl in die Liste einfügen möchte, muss man 1. einen neuen Knoten erzeugen, in dem wir die Zahl speichern und 2. diesen Knoten in die Liste einhängen • Knoten kann an eine beliebige Position eingefügt werden • Bei „unsortierten Listen“ unterscheidet man zwei Möglichkeiten: a) Einfügen am Anfang der Liste b) Einfügen am Ende der Liste 8/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Einfügen von Elementen am Anfang der Liste A Arbeitsweise der Listenoperation insert(int x): Initialzustand: insert(12); insert(3); head head head 12 3 12 class List { Node head = null; //Liste ist zu Beginn leer ... void insert(int val) { Node p = new Node(val); //Erzeugung eines neuen Knoten p p.next = head; //Der next Zeiger von p wird auf den head gesetzt head = p; //p wird zum neuen Listenanfang } ... }//end class List 9/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Einfügen von Elementen am Ende der Liste A Arbeitsweise der Listenoperation append(int x): Initialzustand: append(12); append(3); head head head 12 12 3 ... void append(int val) { Node p = new Node(val); Node cur = head; if (head == null) head = p; //Liste ist leer else { while (cur.next != null) cur = cur.next; //Suche nach Listenende cur.next = p; //Neuer Knoten wird an das Listenende angehaengt } } ... 10/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Einfügen von Elementen am Ende der Liste B Arbeitsweise der Listenoperation append(int x): Initialzustand: append(12); head head tail tail 12 append(3); tail head 12 3 ... void append(int val) { Node p = new Node(val); if (head == null) head = p; //Liste ist leer else tail.next = p; //Einfuegen des neuen Knoten an das Ende der Liste tail = p; //tail auf das letzte Element setzen } ... 11/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Suchen von Elementen • Wenn ein gesuchtes Element in der Liste enthalten ist, soll ein Zeiger auf den Knoten zurückgegeben werden • Wenn es nicht enthalten ist soll null zurückgegeben werden class List { Node head = null; ... Node search(int val) { Node tmp = head; //Der Knoten tmp wird auf head gesetzt //Durchlaufen der Liste while (tmp!=null && tmp.val!=val) tmp = tmp.next; //Durchlauf ist beendet, falls man am Ende der Liste angekommen ist //oder falls der Knoten tmp den gesuchten Wert enhaelt return tmp; } ... }//end class List 12/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Löschen von Elementen (1/2) • A Schritt 1: Den zu löschende Knoten suchen (delete(3);) del head 12 • 3 8 Schritt 2: Der next Zeiger des Vorgängerknotens muss auf den Nachfolger des zu löschenden Knotens gesetzt werden prev del head 12 3 8 13/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Unsortierte, einfach verkettete Liste Löschen von Elementen (2/2) A prev del class List { head Node head = null; ... 3 8 12 void delete(int val) { Node del = head; Node prev = null; //Suchen des Knotens der geloescht werden soll while (del!=null && del.val!=val) { prev = del; del = del.next; } //Der zu loeschende Knoten wurde gefunden if (del!=null) { //Falls der Knoten auf den head zeigt geloescht werden soll, //muss der Zeigen von head modifiziert werden if (del==head) head = del.next; //Ansonsten: Verwendung des prev Objekts else prev.next = del.next; } } ... }//end class List 14/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Sortierte, einfach verkettete Liste • Reihenfolge der Knoten entspricht der Sortierreihenfolge ihrer Datenfelder • Beispiel einer sortierten Liste: head 8 3 • Klassendiagramm: SortedList head:Node insert(x:int) delete(x:int) search(x:int):Node * 12 Node val: int next: Node 15/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Sortierte, einfach verkettete Liste Einfügen von Elementen Beim Einfügen von Elementen muss die Sortierreihenfolge erhalten bleiben Schritt 1: Einfügeposition suchen (insert(5);) head 8 3 prev 12 cur Schritt 2: Neuen Knoten mit Element einfügen head 5 3 prev newNode 8 12 cur 16/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Sortierte, einfach verkettete Liste Einfügen von Elementen (2/2) void insert(int val) { Node cur = head; Node prev = null; //Suchen der Einfuegeposition while(cur!=null && cur.val<val) { prev = cur; cur = cur.next; } head 5 3 prev newNode 8 12 cur //Erzeugung des neuen Knoten Node newNode = new Node(val); //Einfeugen entweder am Ende der Liste if (p==null) oder //an zwischen den Knoten auf die prev und cur zeigen. //1. next-Zeiger von newNode wird auf das cur-Objekt gesetzt. newNode.next = cur; //2. Falls Liste leer ist muss newNode auf das head-Objekt zugewiesen werden //falls die Liste nicht leer ist, wird der next-Zeiger von prev auf //newNode gesetzt if (cur == head) head = newNode; else prev.next = newNode; }//end method insert 17/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Sortierte, einfach verkettete Liste Suchen von Elementen Statt: tmp.val!=val Node search(int val) { Die komplette Liste muss nicht traversiert werden!!! Node tmp = head; while (tmp != null && tmp.val < val) { tmp = tmp.next; } if (tmp.val==val) return tmp; else return null; }//end method search 18/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Sortierte, einfach verkettete Liste Löschen von Elementen Statt: del.val!=val void delete(int val) { Die komplette Liste muss nicht traversiert werden!!! Node del = head; Node prev = null; //Suchen des Knotens der geloescht werden soll while (del != null && del.val < val) { prev = del; del = del.next; } //Der zu loeschende Knoten wurde gefunden if (del.val==val) { //Falls der Knoten auf den head zeigt geloescht werden soll, //muss der Zeigen von head modifiziert werden if (del==head) head = del.next; //Ansonsten: Verwendung des prev Objekts else prev.next = del.next; } }//end method delete 19/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Beispiel „Stack“ als einfach verkettete Liste (1/4) • Klasse Stack wurde in EH06 mittels eines Arrays realisiert Stack() push(…) stack • pop() length Da wir damals die Klasse Stack mittels Datenabstraktion (durch die Verwendung der Sichtbarkeitsattribute private und public) implementiert wurde, können wir die Implementierung einfach durch eine Liste auswechseln, ohne das Verändrungen in anderen Programmen, die auf unsere Klasse Stack zugreifen vorgenommen werden müssen!!! Stack() push(…) pop() top 20/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Beispiel „Stack“ als einfach verkettete Liste (2/4) public class Stack { //Knoten der Liste: class StackElement { Felder //Array: private String[] stack; private int length; private String value; StackElement next; //List: private StackElement top; Konstruktor //Array: public Stack() { stack = new String[100]; length = 0; } StackElement(String value) { this.value = value; next = null; } } //List (optional): public Stack() { StackElement top = null; } ... } 21/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Beispiel „Stack“ als einfach verkettete Liste (3/4) public class Stack { Einfügen von Elementen in den Stack ... //Array: public void push (String item) { if (length<stack.length) stack[length++] = item; else System.out.println("Stack is full!"); } //List: public void push (String value) { StackElement newElement = new StackElement(value); newElement.next = top; top = newElement; } ... } 22/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Beispiel „Stack“ als einfach verkettete Liste (4/4) public class Stack { ... Löschen von Elementen aus dem Stack //Array: public String pop() { if (length!=0) return stack[--length]; else return null; } //List: public String pop() { if (top!=null) { StackElement tmp = top; top = top.next; return tmp.value; } else return null; } } 23/24 SWE1 Übung – EH8 DI Kerstin Altmanninger Beispiel Sortierte, doppelt verkettete Telefonliste • Beispiel einer sortierten, doppelt verketteten Telefonliste: head • Emilia +43702468 Julian +4367644432 Xaver +436641233 tail Klassendiagramm: PhoneBook name: String head: PhoneBookEntry tail: PhoneBookEntry PhoneBook(name:String) insert(name:String, phoneNumber:String):boolean search(name:String):PhoneBookEntry delete(name:String):boolean update(name:String, phoneNumer:String):boolean findPrevEntry(name:String):PhoneBookEntry print() * PhoneBookEntry name: String phoneNumber: String next: PhoneBookEntry prev: PhoneBookEntry PhoneBookEntry(name:String, phoneNumber:String) toString():String print() lessThan(entry:PhoneBookEntry):boolean lessThan(name:String):boolean greaterThan(entry:PhoneBookEntry):boolean equalsName(entry:PhoneBookEntry):boolean equalsName(name:String):boolean 24/24 SWE1 Übung – EH8 DI Kerstin Altmanninger