MINT lernen

Binäre Suche rekursiv

Aus der Schleife von Kapitel 2 wird ein Selbstaufruf — und jeder Aufruf halbiert das Problem.

1

Der Suchbereich als Parameter

In Kapitel 2 hat eine Schleife die Grenzen links und rechts verschoben. Rekursiv werden die Grenzen als Parameter übergeben: Jeder Aufruf sucht in einem halb so großen Bereich — derselbe Algorithmus, als Selbstaufruf formuliert.

  • Teilproblem:„Suche x in a[links] … a[rechts]“ — die Grenzen sind Hilfsparameter wie in 3.2.1.
  • Abbruch 1:links > rechts: der Bereich ist leer, Rückgabe −1.
  • Abbruch 2:a[mitte] == x: Treffer, Rückgabe mitte.
  • Rekursionsschritt:nur in einer Hälfte weitersuchen — rechts von mitte, wenn a[mitte] < x, sonst links davon.
  • Startmethode:binSuche(a, x) ruft binSuche(a, x, 0, a.length - 1) auf.
  • Endrekursion:jeder Selbstaufruf steht in einem return — deshalb entspricht die Methode genau der Schleife aus Kapitel 2.
static int binSuche(int[] a, int x) {
    return binSuche(a, x, 0, a.length - 1);          // Startaufruf
}

static int binSuche(int[] a, int x, int links, int rechts) {
    if (links > rechts) {
        return -1;                                    // Bereich leer
    }
    int mitte = (links + rechts) / 2;
    if (a[mitte] == x) {
        return mitte;                                 // Treffer
    }
    if (a[mitte] < x) {
        return binSuche(a, x, mitte + 1, rechts);     // rechte Hälfte
    }
    return binSuche(a, x, links, mitte - 1);          // linke Hälfte
}

Aufruf binSuche(a, 48) mit

Aufruflinksrechtsmittea[mitte]Entscheidung
101054242 < 48 → binSuche(a, 48, 6, 10)
261086363 > 48 → binSuche(a, 48, 6, 7)
367648Treffer → gib 6 zurück

Der dritte Aufruf liefert 6; die beiden wartenden Aufrufe reichen den Wert unverändert zurück.

2

Aufrufe zählen

Ziehe den Suchwert \(x\) auf dem Zahlenstrahl (oder wähle ihn mit ←/→). Darüber siehst du für jeden Aufruf den Suchbereich als Balken, die Mitte als Punkt. Vergleiche die Zahl der Aufrufe bei 7, 15 und 31 Elementen; ▶ fährt alle Werte ab.

Suchwert ziehen

Halte fest: Jeder Aufruf halbiert den Bereich. Verdoppelt sich die Zahl der Elemente von 7 auf 15 und auf 31, kommt jeweils nur ein Aufruf mit Vergleich hinzu.

  • Tiefe = Aufrufe:die Methode ruft sich höchstens einmal selbst auf (lineare Rekursion): Die Zahl der Aufrufe ist zugleich die Höhe des Aufrufstapels.
  • Speicher:bei einer Million Elementen genügen gut 20 Rahmen — anders als bei der rekursiven linearen Suche aus 3.1.3 mit einem Rahmen je Element.
  • Teile und herrsche:halbieren und nur eine Hälfte weiterbearbeiten; in 3.3 werden beide Hälften bearbeitet und die Ergebnisse zusammengeführt.
Herleitung:
\(T(n) = T\!\left(\tfrac{n}{2}\right) + 1\)
Ansatz

\(T(n)\): Vergleiche mit a[mitte] im ungünstigsten Fall bei \(n\) Elementen — ein Vergleich, dann ein Aufruf mit höchstens halb so vielen Elementen. Dazu \(T(1) = 1\).

\(= T\!\left(\tfrac{n}{4}\right) + 2\)
einsetzen

Dieselbe Gleichung für \(T(n/2)\) eingesetzt.

\(= T\!\left(\tfrac{n}{2^k}\right) + k\)
\(k\)-mal

Nach \(k\) Halbierungen.

\(\tfrac{n}{2^k} = 1 \;\Leftrightarrow\; k = \log_2 n\)
Bereich 1

So oft lässt sich halbieren, bis ein Element übrig ist.

\(T(n) = \log_2 n + 1\)
Ergebnis

Für \(n\), die keine Zweierpotenz sind: \(\lfloor \log_2 n \rfloor + 1\), z. B. 4 Vergleiche bei \(n = 15\), 5 bei \(n = 31\). Bei erfolgloser Suche kommt der Aufruf mit leerem Bereich hinzu.

Merke

binSuche(a, x, links, rechts): Bereich leer → −1 · Treffer → mitte · sonst Selbstaufruf mit der passenden Hälfte · \(T(n) = T(n/2) + 1\) ⇒ höchstens \(\lfloor \log_2 n \rfloor + 1\) Vergleiche

3

Allgemeine Hinweise

Immer mitte ± 1

Mit binSuche(a, x, mitte, rechts) bleibt ein Bereich aus zwei Elementen gleich groß. In der Schleife war das eine Endlosschleife, rekursiv endet es mit einem StackOverflowError.

Startaufruf prüfen

Die rechte Grenze ist der letzte gültige Index a.length - 1, nicht a.length. Die Startmethode nimmt dem Aufrufer diese Fehlerquelle ab.

Große Reihungen

Bei über einer Milliarde Elementen kann links + rechts den Wertebereich von int überschreiten. Sicher ist mitte = links + (rechts - links) / 2.

Videos