Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Kapitel 3 Elementare Datenstrukturen TexPoint fonts used in EMF.

Ähnliche Präsentationen


Präsentation zum Thema: "Kapitel 3 Elementare Datenstrukturen TexPoint fonts used in EMF."—  Präsentation transkript:

1 Kapitel 3 Elementare Datenstrukturen TexPoint fonts used in EMF.
Read the TexPoint manual before you delete this box.: AA

2 3.1 Grundlegendes Elementare Datenstrukturen: Arrays (Felder)
Verkettete Listen bilden die Bausteine für abstrakte Mechanismen, die ausgehend von niederen Ebenen aufgebaut sind und Algorithmen mit zunehmender Komplexität ermöglichen

3 3.2 Arrays (Felder) Arrays:
Eine feste Sammlung von Daten desselben Typs, die zusammen-hängend gespeichert und über einen Index zugängig sind Fundamentale Datenstruktur: Direktes Abbild des Speichersystems. Ein Arrayzugriff a[i] wird in nur wenige Maschinenanweisungen übersetzt  Programme mit Arrays werden in effiziente Maschineprogramme übersetzt.

4 3.2 Arrays (Felder) Beispiel: Das Primzahlensieb des Eratosthenes
Ältester und bekanntester Siebalgorithmus, benannt nach Eratosthenes (ca. 200 v. Chr.) Prinzip von Siebalgorithmen: Eine Menge von Elementen wird in zwei Klassen aufgeteilt: die guten und die schlechten Elemente. Schlechte Elemente sind einfacher zu finden als gute. Ein Siebprozess eliminiert sukzessive Elemente, die als schlecht erkannt wurden. Jedes eliminierte Element hilft, weitere schlechte Elemente zu erkennen. Die überlebenden Elemente müssen die guten sein. Siebalgorithmus für Primzahlen: gut = ist eine Primzahl schlecht = ist keine Primzahl Der älteste und am besten bekannte Siebalgorithmus ist benannt nach Eratosthenes, der ca. 200 v. Chr. gelebt hat. Siebalgorithmen liegt das folgende Prinzip zugrunde: … Siebalgorithmen sind häufig anwendbar, wenn …

5 3.2 Arrays (Felder) Primzahlensieb von Eratosthenes:
Alle Primzahlen kleiner gleich n ausgeben Markiere die kleinste Primzahl, d.h. 2, und entferne alle ihre Vielfachen innerhalb des gewünschten Bereichs 1 … n. Die kleinste verbleibende Zahl muß prim sein, markiere sie und entferne wieder alle ihre Vielfachen. Wiederhole diesen Prozess für alle Zahlen bis (Falls eine ganze Zahl c <= n faktorisiert werden kann als c = a · b, so muß entweder a <= oder b <= gelten) Das Primzahlensieb von Eratosthenes funktioniert wie folgt: …

6 3.2 Arrays (Felder) Das Primzahlensieb von Eratosthenes funktioniert wie folgt: …

7 3.2 Arrays (Felder) static void eratosthenes(int n) {
int sqrtn = (int) Math.floor(Math.sqrt((double) n)); Sieve s = new Sieve(n); int p = 2; while (p <= sqrtn) { int i = p * p; while (i <= n) { s.remove(i); i = i + p; } do p++; while (! s.isMember(p)); } int c = 0; for (int i = 2; i <= n; i++) if (s.isMember(i)) { c++; System.out.println("prim(" + c + ") = " + i); } } Der JAVA-Code sieht folgendermaßen aus: … Dabei benutzen wir als Sieb ein Objekt der Klasse Sieve, deren Implementierung wir uns gleich näher ansehen werden.

8 3.2 Arrays (Felder) public class Sieve {
public final int n; // Groesse des Sieve private static final int ws = 64; // Anzahl Bit in long private final int as; // Groesse des Arrays private long[] s; // zur Speicherung des Sieve // Konstruktor public Sieve(int n) { this.n = n; as = (n + ws - 1) / ws; s = new long[as]; for (int i = 0; i < as; i++) s[i] = ~0L; }

9 3.2 Arrays (Felder) // public Methoden
/** * Liefert "true" zurueck, falls das Element im * Sieve enthalten ist, sonst "false" */ public boolean isMember(int e) { if ((1 <= e) && (e <= n)) { int i = (e - 1) / ws; e = (e - 1) % ws; return ((s[i] & (1L << e)) != 0); } else return false; }

10 3.2 Verkettete Listen Listenstrukturen = dynamische Datenstrukturen
Mithilfe von Einfüge- und Löschoperationen können Listenstrukturen zur Laufzeit nicht nur die gespeicherten Datenwerte, sondern auch ihre Größe und Struktur verändern Listen: Schlüsselidee Alloziere benötigten Speicherplatz nicht wie beim Array in einem großen zusammenhängenden Teil, sondern dynamisch in kleinen Fragmenten, die ein gegebenes Objekt speichern können Verknüpfung von Datenelementen, die verstreut im Speicher liegen, erfolgt durch Zeiger bzw. Referenzen, d.h. Speicheradressen, an denen die Elemente gegenwärtig abgelegt sind Das Spektrum der Datenstrukturen reicht von statischen Objekten, wie einer Tabelle von Konstanten, zu dynamischen Strukturen wie Listen. Listen erlauben nicht nur, die gespeicherten Datenwerte zu ändern, sondern auch die Größe und Struktur der Liste können sich zur Laufzeit auf Grund von Einfüge- und Löschoperationen oder Reorganisationen verändern. Die meisten der bisher diskutierten Datenstrukturen können ihre Größe und Struktur nur eingeschränkt verändern. Ein zirkulärer Puffer z.B. unterstützt Einfügen an einem und Löschen am anderen Ende und kann bis zu einer vorgegebenen maximalen Größe wachsen. Ein Heap unterstützt Löschen an einem Ende des Arrays, und Einfügen irgendwo im Array. In einer Liste kann jede lokale Veränderung mit einem Aufwand durchgeführt werden, der unabhängig von der Größe der Liste ist - vorausgesetzt wir kennen die Speicherpositionen der involvierten Elemente. Die Schlüsselidee, um diese Forderung zu erfüllen, besteht darin, benötigten Speicherplatz nicht mehr wie beim Array in einem großen zusammenhängenden Teil zu allozieren, sondern den benötigten Speicherplatz dynamisch in kleinen Fragmenten anzulegen, die ein gegebenes Objekt speichern können. Da dann die Datenelemente mehr oder weniger zufallsmäßig verstreut und nicht zusammenhängend im Speicher abgelegt werden, erzeugt eine Einfüge- oder Löschoperation keinen Dominoeffekt, durch den andere Datenelemente herumgeschoben werden. Ein Element wird irgendwo im Speicher abgelegt und mit anderen Elementen durch sogenannte Zeiger oder Referenzen (d.h. Speicheradressen, an denen die Elemente gegenwärtig abgelegt sind) verknüpft.

11 3.2 Verkettete Listen Zeiger oder Referenz
Ein Sprachkonstrukt, das in modernen Programmiersprachen benutzt wird, um das Äquivalent einer Speicheradresse darzustellen Im wesentlichen eine Speicheradresse, kann aber mehr Informationen enthalten: In Java oder anderen streng getypten Sprachen beziehen sich Zeiger bzw. Referenzen auch auf die Typdefinitionen der Objekte, auf die sie verweisen  Übersetzer kann die konsistente Benutzung von Zeiger- bzw. Referenzvariablen sicherstellen Zeigervariable bzw. Referenzvariable nimmt Speicheradressen als Werte an Ein gelöschtes Element hinterläßt keine Lücke, die wie beim Array gefüllt werden müßte. Stattdessen hinterläßt es freien Speicherplatz, der durch einen Speicherplatzverwalter (garbage collector) wieder eingesammelt wird. Ein gelöschtes Element wird wahrscheinlich einige Verbindungslinks aufbrechen, durch die andere Elemente miteinander verknüpft werden. Falls dem so ist, so werden diese Elemente durch neue Links wieder miteinander verknüpft. Ein Zeiger oder eine Referenz ist ein Sprachkonstrukt, das in modernen Programmiersprachen benutzt wird, um das Äquivalent einer Speicheradresse darzustellen. Ein Zeiger bzw. eine Referenz ist im wesentlichen eine Speicheradresse, kann aber mehr Informationen enthalten. In Java oder anderen streng getypten Sprachen wie Pascal beziehen sich Zeiger bzw. Referenzen auch auf die Typdefinitionen der Objekte, auf die sie verweisen. Dies ermöglicht dem Übersetzer, die konsistente Benutzung von Zeiger- bzw. Referenzvariablen sicherzustellen. Eine Zeigervariable bzw. Referenzvariable nimmt als Werte Speicheradressen an.

12 3.2 Verkettete Listen Definition: Eine verkettete Liste ist eine Menge von Elementen, bei der jedes Element zu einem Knoten gehört, der auch eine Verbindung zu einem Knoten enthält. head tail null e1 e2 ei ... ei+1 en public class ListNode<E> { private E element = null; private ListNode<E> next = null; public ListNode() { }; public ListNode(E element) { this.element = element; } Illustrieren wir diese Konzepte am Beispiel der linearen Liste: Eine lineare Liste ist eine Folge von Knoten. Der erste Knoten ist der Kopf der Liste, der letzte der Schwanz. Abgesehen vom Schwanz zeigt jeder Knoten auf den nachfolgenden. Da der Schwanz keinen Nachfolger hat, erhält sein Zeiger den vordefinierten Wert null. Zugriff auf die Liste erfolgt über einen externen Zeiger head. Ist die Liste leer, so hat head den Wert null. Ein Listenknoten speichert ein Datenelement ei und einen Zeiger auf den nachfolgenden Listenknoten. Das Klasseninterface eines Knotens in Java könnte folgendermaßen aussehen: …

13 3.2 Verkettete Listen public void setNextNode(ListNode next) { this.next = next; } public ListNode<E> getNextNode() { return next; } public void setData(E element) { this.element = element; } public E getData() { return element; } } // class ListNode Implementierung Fortsetzung …

14 3.2 Verkettete Listen Einfügen eines neuen Knotens als Nachfolger eines Knotens, gegeben durch den Zeiger p: q = new ListNode<E>(y); q.setNextNode(p.getNextNode()); p.setNextNode(q); y q q.next = p.next; p.next = q; Abkürzende Schreibweise, die aber nicht den Grundsätzen der objekt-orientierten Programmierung (Zugriff auf in einer Klasse gekapselte Attribute nur über spezielle Zugriffsmethoden!) entspricht und daher so nicht in Programmen verwendet werden sollte! Lokale Operationen, wie das Einfügen oder Löschen an einer durch einen Zeiger p vorgegebenen Position, lassen sich effizient durchführen. Durch die folgenden Anweisungen wird ein neuer Knoten als Nachfolger eines Knotens eingefügt, auf den der Zeiger p verweist: … Im folgenden werde ich eine abgekürzte Schreibweise benutzen, die … head tail null e1 e2 ei ... ei+1 en p

15 3.2 Verkettete Listen Löschen des Nachfolgers eines Knotens, gegeben durch den Zeiger p: p.next = p.next.next; head tail null e1 e2 ei–1 ... ei en ei+1 p Der Nachfolger eines Knotens, auf den der Zeiger p verweist, wird durch die folgende Anweisung gelöscht: …

16 3.2 Verkettete Listen Erzeugung einer Liste:
ListNode<Integer> head, tmp; head = new ListNode<Integer>(0); tmp = head; for (int i = 1; i < 4; i++) { tmp.next = new ListNode<Integer>(i); tmp = tmp.next; } tmp head null i 1 2 null 3 1 null 2 3 null tmp tmp tmp Betrachten wir als Beispiel eine Liste, deren Elemente vom primitiven Datentyp int sind … Eine solche Liste können wir z.B. folgendermaßen erzeugen: … Will man die Listenelemente ausgeben, so erreicht man dies durch Traversierung: …

17 3.2 Verkettete Listen Traversierung der Liste (Ausgabe):
tmp = head; while (tmp != null) { System.out.println(tmp.data + " "); tmp = tmp.next; }  Ausgabe: tmp tmp Betrachten wir als Beispiel eine Liste, deren Elemente vom primitiven Datentyp int sind … Eine solche Liste können wir z.B. folgendermaßen erzeugen: … Will man die Listenelemente ausgeben, so erreicht man dies durch Traversierung: …


Herunterladen ppt "Kapitel 3 Elementare Datenstrukturen TexPoint fonts used in EMF."

Ähnliche Präsentationen


Google-Anzeigen