zu den Aufgaben im Kapitel 5
Aufgabe 5.2: Welche sind „die wichtigsten Operationen“ bei jedem dieser Verfahren? Welchen Einfluss haben die Verbesserungen auf die Zeitkomplexität?
Lösung: Der Vergleich zweier Elemente und der Austausch zweier benachbarten Elemente. Die Verbesserung hat die Zeitkomplexität nicht verbessert, nur den Zeitverbrauch (um eine Konstante).
Aufgabe 5.3: Verbessern Sie die Prozedur straight insertion um das binäre Suchen, in dem die Suche nach der Einfügestelle in der Sequenz nicht sequenziell erfolgt, sondern nach der Strategie teile und herrsche.
Lösung:
private int suchen(Element[] sammlung, Element zumSuchen, int bis) { int links = 0; int rechts = bis; while (links < rechts) { int mitte = (links + rechts) / 2; if (zumSuchen.less(sammlung[mitte])) rechts = mitte-1; else links = mitte+1; } if (zumSuchen.less(sammlung[links]) && links > 0) links--; if (sammlung[links].less(zumSuchen)) links++; return links; }
void straightInsertionBinary(Element[] sammlung) { for (int index = 1; index < sammlung.length; index++) { final Element elementZumEinfuegen = sammlung[index]; // zuerst Einfügestelle suchen: int einfuegestelle = suchen(sammlung, elementZumEinfuegen, index); for (int j = index; j > einfuegestelle; j--) // nach oben schieben: sammlung[j] = sammlung[j-1]; sammlung[einfuegestelle] = elementZumEinfuegen; } }
Aufgabe 5.4: Führen Sie die Prozedur shellSort aus, nachdem Sie sie durch Ausgaben über System.out.println ergänzt haben. Sie können damit ihre Arbeitsweise verstehen. Führen Sie den Algorithmus mit 25 Zahlen auf Papier durch und vergleichen Sie Ihr Ergebnis mit der Ausgabe.
Lösung:
Shell sort:
kowdvjpaeiqhclyfmrubsgxnt
Schrittweite = 7
aowdvjpkeiqhclyfmrubsgxnt
aewdvjpkoiqhclyfmrubsgxnt
aeidvjpkowqhclyfmrubsgxnt
aeidvjpkowqhclyfmrubsgxnt
aeidhjpkowqvclyfmrubsgxnt
aeidhcpkowqvjlyfmrubsgxnt
aeidhclkowqvjpyfmrubsgxnt
aeidhclkowqvjpyfmrubsgxnt
aeidhclkfwqvjpyomrubsgxnt
aeidhclkfmqvjpyowrubsgxnt
aeidhclkfmqvjpyowrubsgxnt
aeidhclkfmqujpyowrvbsgxnt
aeidhblkfmqucpyowrvjsgxnt
aeidhblkfmqucpyowrvjsgxnt
aeidhblgfmqucpkowrvjsyxnt
aeidhblgfmqucpkowrvjsyxnt
aeidhblgfmqucpkonrvjsyxwt
aeidhblgfmqucpkonrvjsyxwt
Schrittweite = 3
aeidhblgfmqucpkonrvjsyxwt
aeidhblgfmqucpkonrvjsyxwt
aebdhilgfmqucpkonrvjsyxwt
aebdhilgfmqucpkonrvjsyxwt
aebdgilhfmqucpkonrvjsyxwt
aebdgflhimqucpkonrvjsyxwt
aebdgflhimqucpkonrvjsyxwt
aebdgflhimqucpkonrvjsyxwt
aebdgflhimqucpkonrvjsyxwt
aebcgfdhilqumpkonrvjsyxwt
aebcgfdhilpumqkonrvjsyxwt
aebcgfdhilpkmquonrvjsyxwt
aebcgfdhilpkmquonrvjsyxwt
aebcgfdhilnkmpuoqrvjsyxwt
aebcgfdhilnkmproquvjsyxwt
aebcgfdhilnkmproquvjsyxwt
aebcgfdhiljkmnropuvqsyxwt
aebcgfdhiljkmnropsvquyxwt
aebcgfdhiljkmnropsvquyxwt
aebcgfdhiljkmnropsvquyxwt
aebcgfdhiljkmnropsvquyxwt
aebcgfdhiljkmnropstquvxwy
Schrittweite = 1
aebcgfdhiljkmnropstquvxwy
abecgfdhiljkmnropstquvxwy
abcegfdhiljkmnropstquvxwy
abcegfdhiljkmnropstquvxwy
abcefgdhiljkmnropstquvxwy
abcdefghiljkmnropstquvxwy
abcdefghiljkmnropstquvxwy
abcdefghiljkmnropstquvxwy
abcdefghiljkmnropstquvxwy
abcdefghijlkmnropstquvxwy
abcdefghijklmnropstquvxwy
abcdefghijklmnropstquvxwy
abcdefghijklmnropstquvxwy
abcdefghijklmnropstquvxwy
abcdefghijklmnorpstquvxwy
abcdefghijklmnoprstquvxwy
abcdefghijklmnoprstquvxwy
abcdefghijklmnoprstquvxwy
abcdefghijklmnopqrstuvxwy
abcdefghijklmnopqrstuvxwy
abcdefghijklmnopqrstuvxwy
abcdefghijklmnopqrstuvxwy
abcdefghijklmnopqrstuvwxy
abcdefghijklmnopqrstuvwxy
abcdefghijklmnopqrstuvwxy
Aufgabe 5.5: Geben Sie eine Reihung der Länge 8 an, welche „besonders günstige Daten“ für quickSort enthält. Welchen Zeitbedarf hat quickSort in einem solchen günstigsten Fall?
Lösung: Günstige Fälle sind diejenigen, wo das mittlere Element den mittleren Wert enthält. Besonders günstige Daten:
a b c d e f g h
Zeitbedarf: 3 Schritte.
Aufgabe 5.6: Geben Sie eine Reihung der Länge 8 an, welche „besonders ungünstige Daten“ für quickSort enthält. Welchen Zeitbedarf hat quickSort in einem solchen ungünstigsten Fall?
Lösung: Ungünstige Daten sind diejenigen, wo sich der mittlere Wert am Rand befindet. Besonders ungünstige Daten:
d b a c g h f e
Zeitbedarf: 8 Schritte.
Aufgabe 5.7: Wie groß muss der Stapel für die Ausführung von quickSort im günstigsten bzw. im ungünstigsten Fall bzw. durchschnittlich sein? Geben Sie die Größe des Stapels in QUICK-Speichereinheiten an.
Lösung: Im günstigsten Fall n log2 n QUICK-Speichereinheiten, im ungünstigsten Fall n QUICK-Speichereinheiten.
Aufgabe 5.8: Warum ist die folgende Reihung keine Halde?
sammlung | M | K | J | J | K | H | I | F | G | G | F | G | I |
Indices: | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
Lösung: K auf Index 2 ist größer als J auf Index 3
Aufgabe 5.9: Geben Sie eine Halde mit sieben Komponenten an, die die Schlüssel A, B, C, D, E, F und G haben.
Lösung:
sammlung | G | E | F | D | C | B | A |
Indices: | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Aufgabe 5.10: Wenden Sie diesen Algorithmus auf die folgenden Reihungen an:
X | Y | U | A | B | A | C |
A | K | M | D | B | B | C |
A | B | B | B | B | B | B |
Lösung:
X | Y | U | A | B | A | C |
Y | X | U | A | B | A | C |
A | K | M | D | B | B | C |
M | K | A | D | B | B | C |
M | K | C | D | B | B | A |
A | B | B | B | B | B | B |
B | B | A | B | B | B | B |
B | B | B | B | B | B | A |
Aufgabe 5.11: Beantworten Sie folgende Fragen: Welche Fälle sind für die Prozedur senken besonders günstig? Welche sind besonders ungünstig? Welchen Zeitbedarf hat die Prozedur senken im günstigsten und im ungünstigsten Fall? Welchen Zeitbedarf hat der oben beschriebene Algorithmus, der eine Reihung in eine Halde umwandelt („1. Schritt“), im ungünstigsten Fall? Welchen Zeitbedarf hat der oben beschriebene Algorithmus, der eine Halde in eine sortierte Reihung umwandelt („2. Schritt“), im ungünstigsten Fall? Welchen Zeitbedarf hat die Prozedur heapSort im ungünstigsten Fall?
Lösung: Besonders günstig ist der Fall, wenn reihung eine Halde ist – dann muss senken nichts tun. Besonders ungünstig ist der Fall, wenn an der Spitze der „fast-Halde“ ein kleineres Element steht, als die Hälfte der Elemente (z.B. das kleinste). Dann muss senken es in log2 n Schritten in die 2. Hälfte von reihung transportieren.
Aufgabe 5.12: Ergänzen Sie das obige Programm durch Ausgaben (System.out.println), damit Sie seine Arbeitsweise verstehen können. Führen Sie die Algorithmen mit 20 Zahlen auf Papier durch und vergleichen Sie Ihre Ergebnisse mit der Ausgabe.
Lösung:
Heap sort:
kowdvjpaeiqhclyfmrubsgxnt
Halde aufbauen:
kowdvjpaeiqtclyfmrubsgxnh
kowdvjpaeixtclyfmrubsgqnh
kowdvjpaesxtclyfmrubigqnh
kowdvjpausxtclyfmrebigqnh
kowdvjpmusxtclyfarebigqnh
kowdvjymusxtclpfarebigqnh
kowdvtymusxnclpfarebigqjh
kowdxtymusvnclpfarebigqjh
kowuxtymrsvnclpfadebigqjh
koyuxtwmrsvnclpfadebigqjh
kxyuvtwmrsqnclpfadebigojh
yxwuvtpmrsqnclkfadebigojh
Halde abbauen:
xvwustpmriqnclkfadebhgojy
wvtusnpmriqjclkfadebhgoxy
vutrsnpmoiqjclkfadebhgwxy
ustrqnpmoigjclkfadebhvwxy
tsprqnlmoigjchkfadebuvwxy
srpoqnlmeigjchkfadbtuvwxy
rqpoinlmebgjchkfadstuvwxy
qopminlfebgjchkdarstuvwxy
ponmijlfebgachkdqrstuvwxy
omnfijldebgachkpqrstuvwxy
nmlfijkdebgachopqrstuvwxy
milfhjkdebgacnopqrstuvwxy
likfhjcdebgamnopqrstuvwxy
kijfhacdebglmnopqrstuvwxy
jigfhacdebklmnopqrstuvwxy
ihgfbacdejklmnopqrstuvwxy
hfgebacdijklmnopqrstuvwxy
gfdebachijklmnopqrstuvwxy
fedcbaghijklmnopqrstuvwxy
ecdabfghijklmnopqrstuvwxy
dcbaefghijklmnopqrstuvwxy
cabdefghijklmnopqrstuvwxy
bacdefghijklmnopqrstuvwxy
abcdefghijklmnopqrstuvwxy
abcdefghijklmnopqrstuvwxy
Aufgabe 5.13: Programmieren Sie dieses Verfahren. Entwickeln Sie die Klasse Sortierkanal mit folgender Spezifikation:
interface Element { boolean less(final Element element); }
abstract class Sortierkanal { public Sortierkanal(int groesse) { ... } public void sortierkanal() { ... } // ruft lesen öfter auf, um das nächste Element einzulesen // ruft schreiben öfter auf, um das kleinste gelesene Element auszugeben // ruft sequenzEnde, wenn das auszugebende Element kleiner als das letzte abstract Element lesen() throws Ende; abstract void schreiben(final Element element); abstract void sequenzEnde(); }
Lösung:
abstract class ASortierkanal { abstract Element lesen() throws EndeException; abstract void schreiben(final Element element); abstract void sequenzEnde();
private Element[] halde; public ASortierkanal(int groesse) { halde = new Element[groesse+1]; // Platz 0 bleibt unbenutzt }
private class Halde { // operiert an der Reihung halde private int anfang, ende; // aktuelle Grenzen der Halde innerhalb der Reihung halde Halde() { anfang = halde.length; ende = anfang - 1; } boolean istLeer() { return anfang > ende; } boolean istVoll() { return anfang == 1; } Element spitze() { return halde[anfang]; } void senken() { // halde[anfang..ende] wird durch das senken des Elements halde[anfang-1] // zur halde[anfang-1..ende] erweitert. int index = anfang - 1; // linke Grenze der neuen Halde int nachfolger = anfang; final Element zuSenken = halde[index]; // neues Element while(true) { if (nachfolger > ende) break; // weil nachfolger nicht existiert if (nachfolger < ende && halde[nachfolger+1].less(halde[nachfolger])) // größeren nachfolger oder nachfolger+1 auswählen nachfolger ++; if (zuSenken.less(halde[nachfolger])) break; // Platz gefunden halde[index] = halde[nachfolger]; // hochrücken index = nachfolger; // runtergehen nachfolger = 2 * index - anfang + 1; // linker nachfolger, wenn existiert } halde[index] = zuSenken; // Element einfügen anfang --; // Halde wurde nach unten erweitert } void fuellen(Element zumEinfuegen) { // fügt Element in die halde[anfang .. ende] ein, // indem sie entweder zu halde[anfang - 1 .. ende] verlängert // oder das Spitzenelement ausgegeben wird if (istVoll()) { schreiben(spitze()); zuletztGeschrieben = spitze(); anfang++; // Platz machen } halde[anfang-1] = zumEinfuegen; // neues Element eintragen senken(); } void entleeren() { // gibt das Element halde[anfang] aus und erzeugt die halde[anfang .. ende-1], // indem halde[ende] gesenkt wird schreiben(spitze()); zuletztGeschrieben = spitze(); halde[anfang] = halde[ende] ; // hinteres Element anfang ++; // neues Element noch nicht gesenkt ende --; // Halde hinten verkürzen senken(); // in der verkürzten Halde } }
private Element zuletztGeschrieben; void sortierkanal() { Element aktuelles; // das aktuelle Element boolean erstes = true; // noch kein Element wurde geschrieben final int grenze = halde.length-1; Halde aktiv = new Halde(), ersatz = new Halde(); // zwei leere Halden try { while (true) { // Aussprung bei EndeException von lesen() aktuelles = lesen(); if (! aktiv.istVoll()) // Halde noch nicht voll, aufbauen: aktiv.fuellen(aktuelles); else if (erstes || ! aktuelles.less(zuletztGeschrieben)) { // aktuelles gehört dieser Sequenz erstes = false; if (aktuelles.less(aktiv.spitze()) && !aktuelles.less(zuletztGeschrieben)) { // sofort ausgeben: schreiben(aktuelles); zuletztGeschrieben = aktuelles; } else { // wurde schon kleineres ausgegeben aktiv.fuellen(aktuelles); // fuellen ruft schreiben auf, wenn Halde voll } // nicht sofort ausgeben } else { // aktuelles gehört der nächsten Sequenz, in die Ersatzhalde: if (!aktiv.istLeer()) { aktiv.entleeren(); // in der aktiven Halde wird Platz gemacht: // entleeren ruft schreiben auf und verkürzt die aktive Halde ersatz.fuellen(aktuelles); } else { // aktiv.istLeer() sequenzEnde(); // aktuelle Sequenz ist zu Ende aktiv = ersatz; ersatz = new Halde(); aktiv.fuellen(aktuelles); // fuellen ruft schreiben auf, wenn Halde voll } } } // Leseschleife } catch (EndeException e) { } // einlesen fertig // alle Eingabedaten wurden eingelesen; zuerst aktive Halde abbauen: if (aktiv.spitze().less(zuletztGeschrieben)) sequenzEnde(); while (! aktiv.istLeer()) aktiv.entleeren(); // Ersatzhalde abbauen: sequenzEnde(); while (! ersatz.istLeer()) ersatz.entleeren(); sequenzEnde(); } }
Aufgabe 5.14: Entwickeln Sie eine Tabelle für k = 4, ähnlich der Tabelle 5.14. Zeichnen Sie das Verfahren, wie 100 Sequenzen nach dem Fibonacci-Algorithmus gemischt werden.
Lösung: fehlt noch