Lektion 6 von 8

Tauschen, Sortieren, Unterprogramme

Drei Dinge, die in Prüfungsaufgaben zu Feldern oft zusammen auftreten: zwei Werte tauschen, ein Feld mit Bubblesort sortieren und Abläufe in Unterprogramme auslagern. Mit dieser Lektion hast du alle Bausteine beisammen, die ein Struktogramm in der AP1 und AP2 typischerweise braucht.

  • Etwa 40 Minuten
  • 1 Leseaufgabe, 1 Ergänzungsaufgabe, 2 Zeichenaufgaben, 4 Quizfragen
  • Papier und Stift genügen

Zwei Werte tauschen

In a steht 5, in b steht 8, und danach soll es umgekehrt sein. Der naheliegende Versuch a = b, dann b = a geht schief. Erinnere dich an Lektion 2: Eine Zuweisung überschreibt den alten Wert der Variablen links, und der ist danach weg.

Falsch: der alte Wert geht verloren

Tauschversuch
a = b
b = a

Richtig: mit Hilfsvariable

Tauschen
hilf = a
a = b
b = hilf
Anweisungabhilf
Start58·
hilf = a585
a = b885
b = hilf855
Der Tausch mit Hilfsvariable im Schreibtischtest (Wertetabelle). Nach a = b steht die 8 zweimal da; die 5 ist nur noch in hilf und wird von dort zurückgeholt.

Ohne hilf stünde nach a = b in beiden Variablen die 8, und b = a wäre wirkungslos. Der Tausch ist also immer ein Dreischritt:

hilf = a
a = b
b = hilf

Die Hilfsvariable heißt in Lösungshinweisen oft hilf, temp oder tausch; der Name ist egal, die drei Zeilen nicht. Bei einem Feld (Array) tauschst du genauso, nur mit indizierten Elementen: hilf = zahlen[i], zahlen[i] = zahlen[i + 1], zahlen[i + 1] = hilf. Genau das braucht das Sortieren.

Bubblesort

Bubblesort sortiert ein Feld aufsteigend nach einer einfachen Idee: Gehe das Feld von vorn nach hinten durch und vergleiche jedes Element mit seinem rechten Nachbarn. Steht links der größere Wert, tausche die beiden. Nach einem solchen Durchlauf ist das größte Element ganz hinten angekommen; es ist wie eine Luftblase nach oben gestiegen, daher der Name. Dann beginnt der nächste Durchlauf, der nur noch bis vor das bereits einsortierte Ende gehen muss.

Struktogramm

Bubblesort
für durchlauf = 1 bis n - 1
für i = 1 bis n - durchlauf
zahlen[i] > zahlen[i + 1]janein
hilf = zahlen[i]
zahlen[i] = zahlen[i + 1]
zahlen[i + 1] = hilf

Pseudocode

FÜR durchlauf = 1 BIS n - 1
    FÜR i = 1 BIS n - durchlauf
        WENN zahlen[i] > zahlen[i + 1] DANN
            hilf = zahlen[i]
            zahlen[i] = zahlen[i + 1]
            zahlen[i + 1] = hilf
        ENDE WENN
    ENDE FÜR
ENDE FÜR

Zwei Zählschleifen ineinander. Die äußere zählt die Durchläufe: Bei n Elementen reichen n − 1, weil nach jedem Durchlauf ein weiteres Element hinten sicher steht und das letzte übrig bleibende von selbst richtig liegt. Die innere Schleife vergleicht die Nachbarpaare und läuft nur bis n − durchlauf, weil dahinter schon sortiert ist. Der Vergleich zahlen[i] > zahlen[i + 1] greift bis auf zahlen[n - durchlauf + 1] zu, im ersten Durchlauf also bis zum letzten Element; das ist der Grund, warum die innere Grenze eins kleiner sein muss als die Anzahl der noch unsortierten Elemente.

Der Schreibtischtest für zahlen = [5, 2, 4, 1] mit n = 4, das erste Element hat den Index 1. Die Spalte „Feld danach“ zeigt den Zustand nach dem jeweiligen Vergleich:

durchlaufiVergleichTausch?Feld danach
115 > 2ja[2, 5, 4, 1]
125 > 4ja[2, 4, 5, 1]
135 > 1ja[2, 4, 1, 5]
212 > 4nein[2, 4, 1, 5]
224 > 1ja[2, 1, 4, 5]
312 > 1ja[1, 2, 4, 5]

Nach Durchlauf 1 steht die 5 hinten, nach Durchlauf 2 die 4 an vorletzter Stelle, nach Durchlauf 3 ist das Feld sortiert. Die innere Schleife hat 3, 2 und 1 Durchläufe, zusammen 6 Vergleiche; allgemein sind es n · (n − 1) / 2. Das musst du nicht herleiten können, aber es erklärt, warum Bubblesort bei großen Feldern langsam ist und in Lehrbüchern und Aufgaben trotzdem verbreitet ist: Er ist kurz und lässt sich gut nachvollziehen.

Unterprogramme: Funktion und Prozedur

Die Grundmuster aus Lektion 5 tauchen in größeren Aufgaben mehrfach auf. Statt sie jedes Mal neu hinzuschreiben, lagerst du sie in ein Unterprogramm aus: ein eigenes Struktogramm mit Namen, das vom Hauptprogramm aufgerufen wird. Zwei Arten gibt es:

  • Eine Funktion liefert ein Ergebnis zurück. Die letzte Anweisung ist typischerweise Rückgabe …, und der Aufruf steht dort, wo man den Wert braucht: in einer Zuweisung oder in einer Bedingung.
  • Eine Prozedur tut etwas (gibt aus, verändert ein Feld), liefert aber nichts zurück. Sie wird als eigene Anweisung aufgerufen.

Der Titel des Struktogramms ist die Signatur: Name, in Klammern die Parameter mit Datentyp, und bei Funktionen nach dem Doppelpunkt der Rückgabetyp. Parameter sind die Werte, die der Aufrufer hineingibt; im Unterprogramm benutzt du sie wie Variablen.

Struktogramm

istGerade(zahl: Ganzzahl): Wahrheitswert
zahl MOD 2 == 0janein
Rückgabe wahr
Rückgabe falsch

Pseudocode

FUNKTION istGerade(zahl: Ganzzahl): Wahrheitswert
    WENN zahl MOD 2 == 0 DANN
        RÜCKGABE wahr
    SONST
        RÜCKGABE falsch
    ENDE WENN
ENDE FUNKTION

istGerade(7) liefert falsch, istGerade(10) wahr. Kürzer wäre eine einzige Anweisung Rückgabe zahl MOD 2 == 0; beides ist richtig. Die Summe aus Lektion 5 als Funktion mit zwei Parametern:

Struktogramm

summe(zahlen: Feld, n: Ganzzahl): Ganzzahl
ergebnis = 0
für i = 1 bis n
ergebnis = ergebnis + zahlen[i]
Rückgabe ergebnis

Pseudocode

FUNKTION summe(zahlen: Feld, n: Ganzzahl): Ganzzahl
    ergebnis = 0
    FÜR i = 1 BIS n
        ergebnis = ergebnis + zahlen[i]
    ENDE FÜR
    RÜCKGABE ergebnis
ENDE FUNKTION

Beachte den Namen ergebnis für den Akkumulator: Die Variable darf nicht so heißen wie die Funktion selbst, sonst wird es beim Lesen unklar. Und eine Prozedur, die ein Feld ausgibt:

Struktogramm

ausgabeFeld(zahlen: Feld, n: Ganzzahl)
für i = 1 bis n
Ausgabe zahlen[i]

Pseudocode

PROZEDUR ausgabeFeld(zahlen: Feld, n: Ganzzahl)
    FÜR i = 1 BIS n
        AUSGABE zahlen[i]
    ENDE FÜR
ENDE PROZEDUR

Der Aufruf im Hauptprogramm

Für den Aufruf einer Prozedur ist ein eigener Kasten mit doppelten Seitenlinien üblich. Das Ergebnis einer Funktion wird dagegen meist in einer normalen Anweisung verwendet, also zugewiesen oder direkt in einer Bedingung geprüft:

Struktogramm

Hauptprogramm
ausgabeFeld(zahlen, n)
gesamt = summe(zahlen, n)
Ausgabe gesamt
istGerade(gesamt)janein
Ausgabe "Summe ist gerade"
Ausgabe "Summe ist ungerade"

Pseudocode

ausgabeFeld(zahlen, n)
gesamt = summe(zahlen, n)
AUSGABE gesamt
WENN istGerade(gesamt) DANN
    AUSGABE "Summe ist gerade"
SONST
    AUSGABE "Summe ist ungerade"
ENDE WENN

Beim Aufruf stehen die Argumente in derselben Reihenfolge wie die Parameter in der Signatur: summe(zahlen, n) übergibt das Feld an zahlen und die Anzahl an n. Für zahlen = [4, 9, 2, 7] gibt das Hauptprogramm 4, 9, 2, 7 aus, dann 22, dann „Summe ist gerade“. Ob du den Funktionsaufruf mit Zuweisung als normale Anweisung oder ebenfalls im Aufrufkasten zeichnest, ist in vielen Lösungshinweisen gleichwertig; wichtig ist, dass der Rückgabewert irgendwo landet und nicht verloren geht. Wird ein Feld übergeben, bekommt das Unterprogramm das Feld selbst, keine Kopie: Änderungen an den Elementen, etwa durch ein Sortieren, wirken nach außen und sind nach dem Aufruf im Hauptprogramm sichtbar.

Übungen

Leseaufgabe 6.1

Führen Sie einen Schreibtischtest durch: Das Feld zahlen = [7, 3, 9, 1] wird mit dem Bubblesort aus dieser Lektion aufsteigend sortiert; das erste Element hat den Index 1, n = 4. Geben Sie den Zustand des Feldes nach dem ersten und nach dem zweiten Durchlauf der äußeren Schleife an. Notiere jeden Vergleich einzeln.

Musterlösung anzeigen
durchlaufiVergleichTausch?Feld danach
117 > 3ja[3, 7, 9, 1]
127 > 9nein[3, 7, 9, 1]
139 > 1ja[3, 7, 1, 9]
213 > 7nein[3, 7, 1, 9]
227 > 1ja[3, 1, 7, 9]

Nach dem ersten Durchlauf: [3, 7, 1, 9], die 9 ist hinten. Nach dem zweiten Durchlauf: [3, 1, 7, 9], die 7 steht an vorletzter Stelle. Der dritte Durchlauf tauscht noch 3 und 1, Endzustand [1, 3, 7, 9]. Typischer Fehler: im zweiten Durchlauf noch einmal bis i = 3 vergleichen; die innere Schleife läuft nur bis n − 2 = 2.

Ergänzungsaufgabe 6.2

Ergänzen Sie das Struktogramm an den Stellen (1) und (2), damit es das Feld zahlen mit n Elementen aufsteigend sortiert; das erste Element hat den Index 1. Lücke (1) ist der Kopf der inneren Zählschleife, Lücke (2) die Bedingung, unter der getauscht wird.

Bubblesort
für durchlauf = 1 bis n - 1
(1)
(2)janein
hilf = zahlen[i]
zahlen[i] = zahlen[i + 1]
zahlen[i + 1] = hilf
Musterlösung anzeigen
Bubblesort
für durchlauf = 1 bis n - 1
für i = 1 bis n - durchlauf
zahlen[i] > zahlen[i + 1]janein
hilf = zahlen[i]
zahlen[i] = zahlen[i + 1]
zahlen[i + 1] = hilf

(1) für i = 1 bis n - durchlauf: Im ersten Durchlauf bis n − 1, weil der Vergleich auf zahlen[i + 1] zugreift und das bei i = n über das Feldende hinausginge. In jedem weiteren Durchlauf eins weniger, weil hinten schon sortiert ist. (2) zahlen[i] > zahlen[i + 1]: Getauscht wird, wenn links der größere Wert steht; >= in Lücke (2) sortiert ebenfalls, nur mit unnötigen Tauschen gleicher Werte. Gleichwertig, nur langsamer: die innere Schleife immer bis n − 1 laufen zu lassen; falsch wäre eine Grenze n, weil dann zahlen[n + 1] gelesen würde.

Zeichenaufgabe 6.3

Entwerfen Sie die Funktion anzahlGroesser(zahlen: Feld, n: Ganzzahl, grenze: Ganzzahl): Ganzzahl, die zurückgibt, wie viele Elemente des Feldes größer als die Grenze sind; das erste Element hat den Index 1. Entwerfen Sie außerdem ein Hauptprogramm, das eine Grenze einliest, die Funktion aufruft und das Ergebnis ausgibt.

Musterlösung anzeigen

Funktion

anzahlGroesser(zahlen: Feld, n: Ganzzahl, grenze: Ganzzahl): Ganzzahl
anzahl = 0
für i = 1 bis n
zahlen[i] > grenzejanein
anzahl = anzahl + 1
Rückgabe anzahl

Hauptprogramm

Hauptprogramm
Eingabe grenze
ergebnis = anzahlGroesser(zahlen, n, grenze)
Ausgabe "Anzahl über der Grenze:", ergebnis

Die Funktion ist das Zählmuster aus Lektion 5, nur dass die Grenze als Parameter hereinkommt und die Ausgabe durch Rückgabe anzahl ersetzt ist: Eine Funktion gibt nichts aus, sie liefert. Das Hauptprogramm liest die Grenze ein, ruft die Funktion mit drei Argumenten in der Reihenfolge der Signatur auf, legt das Ergebnis in einer Variablen ab und gibt es aus. Probe mit [4, 9, 2, 7] und grenze = 5: Rückgabe 2.

So wird typischerweise bewertet

  • Signatur als Titel mit drei Parametern und Rückgabetyp Ganzzahl
  • Zähler ab 0, Zählschleife über alle n Elemente, Verzweigung zahlen[i] > grenze
  • Rückgabe des Zählers nach der Schleife, keine Ausgabe in der Funktion
  • Hauptprogramm: Eingabe der Grenze, Aufruf mit Argumenten in Signaturreihenfolge, Ergebnis zugewiesen und ausgegeben
  • Gleichwertig: andere Schleifenart mit denselben Grenzen (ein Zähler ab 0 nur mit Zugriff zahlen[i + 1]), Aufruf direkt in der Ausgabe ohne Zwischenvariable oder im Aufrufkasten
Zeichenaufgabe 6.4

Entwerfen Sie die Funktion istPrimzahl(zahl: Ganzzahl): Wahrheitswert. Sie liefert wahr, wenn zahl eine Primzahl ist, sonst falsch. Prüfe dazu alle möglichen Teiler von 2 bis zahl − 1 mit MOD. Beachte, dass Zahlen kleiner als 2 keine Primzahlen sind. Kontrolliere deine Lösung mit zahl = 1, 2, 7 und 9.

Musterlösung anzeigen
istPrimzahl(zahl: Ganzzahl): Wahrheitswert
zahl < 2janein
istPrim = falsch
istPrim = wahr
für i = 2 bis zahl - 1
zahl MOD i == 0janein
istPrim = falsch
Rückgabe istPrim

Eine Primzahl ist nur durch 1 und sich selbst teilbar. Der Merker istPrim startet mit wahr und kippt auf falsch, sobald ein Teiler zwischen 2 und zahl − 1 gefunden wird. Der Sonderfall zahl < 2 wird vorab abgefangen, weil 0 und 1 keine Primzahlen sind. Für zahl = 2 läuft die Schleife „für i = 2 bis 1“ gar nicht, der Merker bleibt wahr, und das ist richtig. Probe: 9 hat den Teiler 3 (9 MOD 3 == 0), Rückgabe falsch; 7 hat keinen Teiler von 2 bis 6, Rückgabe wahr. Gleichwertig ist eine kopfgesteuerte Schleife „solange i <= zahl − 1 UND istPrim“, die nach dem ersten Teiler abbricht; die Zählschleife darf dafür nicht im Rumpf verändert werden.

So wird typischerweise bewertet

  • Signatur mit Parameter zahl und Rückgabetyp Wahrheitswert
  • Sonderfall zahl < 2 liefert falsch
  • Merker mit Startwert wahr, Schleife von 2 bis zahl − 1 mit Prüfung zahl MOD i == 0
  • Merker im Ja-Zweig auf falsch, Rückgabe des Merkers nach der Schleife
  • Gleichwertig: kopfgesteuerte Schleife mit Abbruch nach dem ersten Teiler, direkte Rückgabe falsch im Ja-Zweig oder Schleife nur bis zur Wurzel von zahl

Jetzt selbst testen

Vier Fragen zu Tauschen, Bubblesort und Unterprogrammen.

1 / 4

a = 3, b = 9. Es werden nacheinander a = b und b = a ausgeführt. Was steht danach in a und b?

2 / 4

Was gilt nach dem ersten Durchlauf der äußeren Schleife eines aufsteigenden Bubblesorts sicher?

3 / 4

Worin unterscheidet sich eine Funktion von einer Prozedur?

4 / 4

Warum läuft die innere Schleife des Bubblesorts nur bis n − durchlauf und nicht bis n?