← Startseite
🔱

Algorithmen Grundlagen V

Array-Manipulation II
Analysieren, Finden, ZusammenfĂŒhren

Grundlagen der Programmierung
Vorlesung 13

Zweithöchstes HÀufigkeit & Duplikate Merge

📋 Heute lernen wir

  • đŸ„ˆ Zweithöchstes/Zweitkleinstes finden
  • 📊 HĂ€ufigkeit zĂ€hlen – Wie oft kommt jeder Wert vor?
  • 🔍 Duplikate finden – Gibt es doppelte Werte?
  • ➕ Arrays zusammenfĂŒhren – Zwei Arrays vereinen
💡 Diese Algorithmen bauen auf Min/Max, ZĂ€hlen und Suchen auf!

đŸ„ˆ Das Zweithöchste finden

Aufgabe: Finde den zweitgrĂ¶ĂŸten Wert!

7[0]
3[1]
12[2]
9[3]
5[4]
đŸ„‡

12

đŸ„ˆ

9

đŸ„ˆ Zweithöchstes: Die Idee

Wir brauchen zwei Variablen:

  • max – das Maximum (đŸ„‡)
  • zweit – das Zweithöchste (đŸ„ˆ)

💡 Algorithmus:

  1. Wenn Element grĂ¶ĂŸer als max:
    → altes max wird zu zweit, neues Element wird max
  2. Sonst wenn Element grĂ¶ĂŸer als zweit (aber kleiner als max):
    → Element wird neues zweit

đŸ„ˆ Zweithöchstes: Schritt fĂŒr Schritt

Array: {7, 3, 12, 9, 5}

iarr[i]Vergleichmaxzweit
Start--7 (arr[0])-∞ (sehr klein)
133 > 7? ❌, 3 > -∞? ✅73
21212 > 7? ✅127 (altes max)
399 > 12? ❌, 9 > 7? ✅129
455 > 12? ❌, 5 > 9? ❌129
Ergebnis: max = 12, zweit = 9 ✓

đŸ„ˆ Zweithöchstes im Code

#include <stdio.h>
#include <limits.h>  // fĂŒr INT_MIN

int main()
{
    int arr[5] = {7, 3, 12, 9, 5};
    int n = 5;
    int max = arr[0];
    int zweit = INT_MIN;  // noch KEIN Kandidat fĂŒr Platz 2

    for (int i = 1; i < n; i++)
    {
        if (arr[i] > max)
        {
            zweit = max;      // Altes max wird zweit
            max = arr[i];     // Neues max
        }
        else if (arr[i] > zweit)
        {
            zweit = arr[i];   // Neues zweit
        }
    }

    printf("Maximum: %d, Zweithöchstes: %d\n", max, zweit);
    return 0;
}
đŸ€” Warum INT_MIN und nicht z. B. 0?

Es heißt nicht "das Ergebnis ist klein". INT_MIN ist nur ein Platzhalter fĂŒr "noch nichts gefunden".

max darf mit arr[0] starten – das erste Element ist ein gĂŒltiger erster Kandidat. FĂŒr zweit gibt es noch keinen: einen "Zweiten" hat man erst, wenn man zwei Elemente gesehen hat.

Die Zeile arr[i] > zweit ĂŒberschreibt nur, wenn das Element grĂ¶ĂŸer ist. Der Platzhalter muss also kleiner als jeder mögliche Wert sein, damit der erste echte Kandidat sicher gewinnt.

⚠ zweit = 0 wĂ€re falsch bei negativen Arrays wie {-5, -9, -3} – kein Element ist > 0.

đŸ„ˆ Analog: Zweitkleinstes

Gleiche Logik, nur mit < statt >:

int min = arr[0];
int zweit = INT_MAX;  // GrĂ¶ĂŸter möglicher Wert

for (int i = 1; i < n; i++)
{
    if (arr[i] < min)
    {
        zweit = min;
        min = arr[i];
    }
    else if (arr[i] < zweit)
    {
        zweit = arr[i];
    }
}
💡 INT_MIN und INT_MAX aus <limits.h> sind die kleinsten/grĂ¶ĂŸten int-Werte, die es ĂŒberhaupt gibt – typisch -2 147 483 648 bzw. +2 147 483 647.
⚠ -999999 ist nicht der kleinste int – nur eine "reicht meistens"-AbkĂŒrzung. Sie klappt nur, solange die Daten nie kleiner werden. INT_MIN ist immer sicher, weil kein int kleiner sein kann → man muss nicht raten, wie klein die Werte werden.

📊 HĂ€ufigkeit zĂ€hlen

Aufgabe: Wie oft kommt jede Zahl vor?

2[0]
5[1]
2[2]
3[3]
5[4]
2[5]
5[6]
3[7]

Ergebnis:

2 kommt 3× vor  Â·  3 kommt 2× vor  Â·  5 kommt 3× vor

📊 HĂ€ufigkeit: Einfache Lösung

FĂŒr einen bestimmten Wert zĂ€hlen (bekanntes Muster!):

#include <stdio.h>

int main()
{
    int arr[8] = {2, 5, 2, 3, 5, 2, 5, 3};
    int n = 8;
    int gesucht = 5;
    int zaehler = 0;

    for (int i = 0; i < n; i++)
    {
        if (arr[i] == gesucht)
        {
            zaehler++;
        }
    }

    printf("%d kommt %d mal vor.\n", gesucht, zaehler);

    return 0;
}
5 kommt 3 mal vor.

📊 Von einem ZĂ€hler zu vielen

Die einfache Lösung zÀhlt nur einen Wert. Was, wenn wir alle gleichzeitig zÀhlen wollen?

Vorher: 1 ZĂ€hler

int zaehler = 0;
...
zaehler++;

Eine Variable. ZĂ€hlt nur die 5. FĂŒr die 2 und die 3 brĂ€uchte ich je eine weitere Variable


Nachher: 10 ZĂ€hler auf einmal

int haeufigkeit[10] = {0};

Diese eine Zeile erzeugt 10 ZĂ€hler, alle auf 0 – einen fĂŒr jede mögliche Zahl 0–9.

Wo ist der ZĂ€hler? Jeder Kasten haeufigkeit[0], haeufigkeit[1], 
 haeufigkeit[9] ist ein eigener ZĂ€hler – genau wie das alte zaehler, nur zehnfach.

đŸȘŁ Die Idee: Ein Eimer pro Zahl

Statt zu suchen, gibt jede mögliche Zahl (0–9) ihren eigenen ZĂ€hler-Eimer. FĂŒr jede Zahl im Array werfen wir einen Stein in ihren Eimer.

Eingabe: arr = {2, 5, 2, 3, 5, 2, 5, 3}  â†’  nacheinander lesen und einsortieren:

0Eimer 0
0Eimer 1
3Eimer 2
2Eimer 3
0Eimer 4
3Eimer 5
0Eimer 6



Steine im Eimer = wie oft die Zahl vorkam. Eimer 2 hat 3 Steine → die 2 kam 3× vor.

Der Dreh: Wert → Adresse
  • Das Etikett des Eimers = welche Zahl (der Index)
  • Der Inhalt des Eimers = wie oft (die Strichliste)
Ablauf pro Element
  • lese 2 → gehe zu Eimer 2 → +1 Stein
  • lese 5 → gehe zu Eimer 5 → +1 Stein
  • 
 ein einziger Durchlauf genĂŒgt!

📊 HĂ€ufigkeit: Alle Werte (mit ZĂ€hler-Array)

Der ZĂ€hler-Array = die Eimer. Der Index ist das Etikett (welche Zahl), der Inhalt die Strichliste (wie oft). Nur fĂŒr kleine positive Zahlen (z.B. 0-9):

#include <stdio.h>

int main()
{
    int arr[8] = {2, 5, 2, 3, 5, 2, 5, 3};
    int n = 8;
    int haeufigkeit[10] = {0};  // ZĂ€hler fĂŒr 0-9, alle auf 0

    /* HÀufigkeiten zÀhlen */
    for (int i = 0; i < n; i++)
    {
        haeufigkeit[arr[i]]++;  // gehe zu Eimer arr[i] → +1 Stein
    }

    /* Ausgabe */
    for (int i = 0; i < 10; i++)
    {
        if (haeufigkeit[i] > 0)
            printf("%d kommt %d mal vor\n", i, haeufigkeit[i]);
    }

    return 0;
}

📊 So fĂŒllt sich der ZĂ€hler-Array

arr = {2, 5, 2, 3, 5, 2, 5, 3} – bei jedem Schritt: haeufigkeit[arr[i]]++

iarr[i]Aktionhaeufigkeit[0..9]
Start–alle 0[0,0,0,0,0,0,0,0,0,0]
02haeufigkeit[2]++[0,0,1,0,0,0,0,0,0,0]
15haeufigkeit[5]++[0,0,1,0,0,1,0,0,0,0]
22haeufigkeit[2]++[0,0,2,0,0,1,0,0,0,0]
33haeufigkeit[3]++[0,0,2,1,0,1,0,0,0,0]
45haeufigkeit[5]++[0,0,2,1,0,2,0,0,0,0]
52haeufigkeit[2]++[0,0,3,1,0,2,0,0,0,0]
65haeufigkeit[5]++[0,0,3,1,0,3,0,0,0,0]
73haeufigkeit[3]++[0,0,3,2,0,3,0,0,0,0]
Ergebnis: haeufigkeit[2]=3, haeufigkeit[3]=2, haeufigkeit[5]=3 → die 2 kam 3×, die 3 kam 2×, die 5 kam 3× vor. Ein einziger Durchlauf!

📊 HĂ€ufigkeit: Visualisierung (das Endergebnis von oben)

arr = {2, 5, 2, 3, 5, 2, 5, 3}

haeufigkeit[] (ZĂ€hler-Array):

0[0]
0[1]
3[2]
2[3]
0[4]
3[5]
0[6]
0[7]
0[8]
0[9]
💡 Der Trick: Wir nutzen den Wert als Index!
  • haeufigkeit[2] = 3 → „2 kommt 3× vor"
  • haeufigkeit[3] = 2 → „3 kommt 2× vor"
  • haeufigkeit[5] = 3 → „5 kommt 3× vor"

🔍 Duplikate finden

Aufgabe: Gibt es Werte, die mehrfach vorkommen?

3[0]
7[1]
2[2]
7[3]
5[4]
1[5]

⚠ Duplikat gefunden!

Die 7 kommt an Position 1 und 3 vor!

🔍 Duplikate: Die Idee

Verschachtelte Schleifen: Vergleiche jedes Element mit allen folgenden!

💡 Algorithmus:

  1. FĂŒr jedes Element i (Ă€ußere Schleife)
  2. Vergleiche mit allen Elementen j > i (innere Schleife)
  3. Wenn arr[i] == arr[j] → Duplikat gefunden!
💡 Warum j > i? Wir mĂŒssen nicht zurĂŒckschauen – das haben wir schon geprĂŒft!

đŸ€ Duplikate: Jeder vergleicht sich mit allen rechts

Wie beim HĂ€ndeschĂŒtteln in einer Reihe: Person i gibt jedem, der rechts von ihr steht, die Hand – dann ist jedes Paar genau einmal dran.

arr = {3, 7, 2, 7}

i steht beivergleicht mit (j > i)
arr[0]=37 · 2 · 7  (3 Vergleiche)
arr[1]=72 · 7 ✅ Treffer!  (2 Vergleiche)
arr[2]=27  (1 Vergleich)
arr[3]=7–  (rechts steht niemand)
Zwei Schleifen
Ă€ußere i = wer schaut?  Â·  innere j = mit wem vergleichen?
Warum nur nach rechts (j > i)?
Das Paar (7,7) einmal zu prĂŒfen reicht – zurĂŒckschauen wĂ€re dieselbe PrĂŒfung nochmal.

🔍 Duplikate im Code

#include <stdio.h>

int main()
{
    int arr[6] = {3, 7, 2, 7, 5, 1};
    int n = 6;
    int hatDuplikate = 0;

    for (int i = 0; i < n - 1; i++)
    {
        for (int j = i + 1; j < n; j++)  // Nur nach rechts schauen!
        {
            if (arr[i] == arr[j])
            {
                printf("Duplikat: %d (Positionen %d und %d)\n",
                       arr[i], i, j);
                hatDuplikate = 1;
            }
        }
    }

    if (!hatDuplikate)
        printf("Keine Duplikate gefunden.\n");

    return 0;
}

🔍 Duplikate: Vergleiche visualisiert

arr = {3, 7, 2, 7}

ij-WerteVergleiche
01, 2, 33==7? ❌, 3==2? ❌, 3==7? ❌
12, 37==2? ❌, 7==7? ✅ Duplikat!
232==7? ❌
Vorteil dieser Methode: Jedes Paar wird nur einmal verglichen!
(i=0,j=1) wird geprĂŒft, aber (i=1,j=0) nicht mehr nötig.

➕ Arrays zusammenfĂŒhren (Merge)

Aufgabe: Zwei Arrays zu einem kombinieren

Array A:

1[0]
2[1]
3[2]

Array B:

4[0]
5[1]

âŹ‡ïž

Ergebnis C:

1[0]
2[1]
3[2]
4[3]
5[4]

➕ Merge: Erst A hinlegen, dann B dahinter

Wie zwei Kartenstapel zu einem: den ersten Stapel ablegen, den zweiten direkt dahinter anlegen. Zwei einfache Kopier-Schleifen.

Schritt 1: A nach C kopieren (PlÀtze 0,1,2)

1c[0]
2c[1]
3c[2]
?c[3]
?c[4]

Schritt 2: B ab Platz nA (=3) anhÀngen

1c[0]
2c[1]
3c[2]
4c[3]
5c[4]
Merke: Das Ergebnis-Array c muss nA + nB PlĂ€tze haben (hier 3+2 = 5) – groß genug fĂŒr beide zusammen.

➕ Erst die Variablen verstehen

Bevor wir den Code lesen: Was ist was? Wir haben drei Arrays und zwei ZĂ€hl-Variablen.

VariableBedeutungWert
aerstes Array{1, 2, 3}
bzweites Array{4, 5}
cErgebnis-Array (noch leer)[ ?, ?, ?, ?, ? ]
nAn = anzahl, A = von Array a → wie viele Elemente in a3
nBn = anzahl, B = von Array b → wie viele Elemente in b2
Zwei Variablen in einer Zeile
int nA = 3, nB = 2; ist nur eine Kurzform fĂŒr:
int nA = 3;
int nB = 2;
Warum ĂŒberhaupt nA und nB?
Damit der Code weiß, wie viele Elemente er kopieren muss und wo B anfĂ€ngt (ab Platz nA).

➕ Merge im Code

#include <stdio.h>

int main()
{
    int a[3] = {1, 2, 3};  // erstes Array
    int b[2] = {4, 5};     // zweites Array
    int c[5];  // Ergebnis, groß genug fĂŒr beide (3+2)
    int nA = 3, nB = 2;  // Anzahl: a hat 3, b hat 2 Elemente

    /* Erst Array A kopieren */
    for (int i = 0; i < nA; i++)
    {
        c[i] = a[i];
    }

    /* Dann Array B anhÀngen */
    for (int i = 0; i < nB; i++)
    {
        c[nA + i] = b[i];  // Ab Position nA einfĂŒgen!
    }

    printf("ZusammengefĂŒhrt: ");
    for (int i = 0; i < nA + nB; i++)
        printf("%d ", c[i]);

    return 0;
}

❓ Warum c[nA + i]?

Nach dem Kopieren von A ist C schon halb gefĂŒllt:

1c[0]
2c[1]
3c[2]
?c[3]
?c[4]

B muss ab Position nA (=3) eingefĂŒgt werden!

i (in B)b[i]nA + ic[nA + i]
043 + 0 = 3c[3] = 4
153 + 1 = 4c[4] = 5

📝 Praxis: Notenverteilung

#include <stdio.h>

int main()
{
    int noten[10] = {2, 1, 3, 2, 1, 4, 2, 3, 5, 1};
    int anzahl[6] = {0};  // ZĂ€hler fĂŒr Noten 1-5 (Index 0 ungenutzt)

    /* Noten zÀhlen */
    for (int i = 0; i < 10; i++)
    {
        anzahl[noten[i]]++;
    }

    /* Balkendiagramm ausgeben */
    printf("Notenverteilung:\n");
    for (int note = 1; note <= 5; note++)
    {
        printf("Note %d: ", note);
        for (int j = 0; j < anzahl[note]; j++)
            printf("*");
        printf(" (%d)\n", anzahl[note]);
    }

    return 0;
}

📝 Ausgabe: Notenverteilung

Notenverteilung: Note 1: *** (3) Note 2: *** (3) Note 3: ** (2) Note 4: * (1) Note 5: * (1)
Kombiniert: HÀufigkeit zÀhlen + Ausgabe mit Schleife!

🧠 Quiz: Zweithöchstes

Was ist das Zweithöchste in {5, 5, 3, 5}?

A) 5
B) 3
C) Kein eindeutiges Zweithöchstes
D) 0

✅ Lösung: Zweithöchstes

Antwort B: 3

Das Maximum ist 5, das Zweithöchste ist 3.

Ablauf mit unserem Algorithmus:

  • Start: max=5, zweit=INT_MIN
  • 5 > 5? Nein, 5 > INT_MIN? Ja → zweit=5
  • 3 > 5? Nein, 3 > 5? Nein
  • 5 > 5? Nein, 5 > 5? Nein

Hmm, unser einfacher Algorithmus wĂŒrde zweit=5 liefern! đŸ€”

⚠ Edge Case: Bei Duplikaten des Maximums mĂŒsste man zusĂ€tzlich prĂŒfen: arr[i] != max

🧠 Quiz: HĂ€ufigkeit

Was ist h[3] nach diesem Code?

int arr[5] = {1, 3, 3, 2, 3};
int h[10] = {0};
for (int i = 0; i < 5; i++) h[arr[i]]++;
A) 1
B) 2
C) 3
D) 5

✅ Lösung: HĂ€ufigkeit

Antwort C: 3

Die 3 kommt dreimal im Array vor!

ZĂ€hlung:

  • arr = {1, 3, 3, 2, 3}
  • h[1]++ → h[1] = 1
  • h[3]++ → h[3] = 1
  • h[3]++ → h[3] = 2
  • h[2]++ → h[2] = 1
  • h[3]++ → h[3] = 3

🧠 Quiz: Merge

A hat 4 Elemente, B hat 3 Elemente.
Wie groß muss Array C mindestens sein?

A) 3
B) 4
C) 7
D) 12

✅ Lösung: Merge

Antwort C: 7

C muss Platz fĂŒr alle Elemente von A und B haben: 4 + 3 = 7

💡 Allgemein: Bei Merge von Arrays mit nA und nB Elementen braucht das Ergebnis-Array nA + nB PlĂ€tze.

📋 Zusammenfassung: Zweithöchstes

int max = arr[0], zweit = INT_MIN;

for (int i = 1; i < n; i++)
{
    if (arr[i] > max)
    {
        zweit = max;   // Altes max → zweit
        max = arr[i];  // Neues max
    }
    else if (arr[i] > zweit)
    {
        zweit = arr[i];
    }
}
  • ✅ Zwei Variablen: max und zweit
  • ✅ Bei neuem max: altes max wird zweit
  • ✅ Ein Durchlauf genĂŒgt!

📋 Zusammenfassung: HĂ€ufigkeit

/* ZĂ€hler-Array fĂŒr Werte 0 bis MAX */
int haeufigkeit[MAX + 1] = {0};

for (int i = 0; i < n; i++)
{
    haeufigkeit[arr[i]]++;  // Wert als Index!
}
  • ✅ Nutzt den Wert als Array-Index
  • ✅ Funktioniert gut fĂŒr kleine, positive Zahlen
  • ✅ Sehr effizient: nur ein Durchlauf!

📋 Zusammenfassung: Duplikate

/* Verschachtelte Schleifen */
for (int i = 0; i < n - 1; i++)
{
    for (int j = i + 1; j < n; j++)  // j startet bei i+1!
    {
        if (arr[i] == arr[j])
            // Duplikat gefunden!
    }
}
  • ✅ Vergleicht jedes Element mit allen folgenden
  • ✅ j startet bei i+1 (nicht bei 0!)
  • ✅ Jedes Paar wird nur einmal verglichen

📋 Zusammenfassung: Merge

/* Erst A kopieren */
for (int i = 0; i < nA; i++)
    c[i] = a[i];

/* Dann B anhÀngen */
for (int i = 0; i < nB; i++)
    c[nA + i] = b[i];  // Ab Position nA!
  • ✅ C muss groß genug sein: nA + nB
  • ✅ Zwei Schleifen: erst A, dann B
  • ✅ B wird ab Index nA eingefĂŒgt

📊 Alle Algorithmen der Serie

VLAlgorithmenSchlĂŒsselkonzepte
9Swap, Min, Maxtemp Variable, Vergleich
10Summe, ZĂ€hlen, ProduktAkkumulator, Startwerte
11Suche, Bubble Sortbreak, verschachtelte Schleifen
12Copy, Reverse, Shift, PalindromZwei-Zeiger-Technik
132nd Max, HĂ€ufigkeit, Duplikate, MergeZĂ€hler-Array, Paarvergleich

🎓 Abschluss der Algorithmen-Serie

🎉 Herzlichen GlĂŒckwunsch!

Sie kennen jetzt die wichtigsten Algorithmen-Bausteine:

  • ✅ Werte tauschen, finden, vergleichen
  • ✅ Daten sammeln und zĂ€hlen
  • ✅ Suchen und sortieren
  • ✅ Arrays manipulieren und analysieren
Diese Muster bilden die Grundlage fĂŒr fast alle komplexeren Algorithmen!

🔗 Weiterlernen & Links

Zum Üben und Nachschauen – besonders hilfreich: Werkzeuge, die Code Schritt fĂŒr Schritt sichtbar machen.

👀 Code visuell durchlaufen

📚 Nachschlagen & Üben

💡 Tipp: Kopiert den HĂ€ufigkeits- oder Merge-Code in pythontutor.com/c und klickt euch durch – ihr seht genau, wie sich der ZĂ€hler-Array Schritt fĂŒr Schritt fĂŒllt.
1 /