#include #include #include #include #include "numbers.h" #include "bintree.h" /** * @brief Vergleichsfunktion für unsigned int-Werte zur Verwendung im Binärbaum. * * Diese Funktion wird von der Binärbaum-Implementierung genutzt, um die * Ordnung der Knoten zu bestimmen. Sie vergleicht die dereferenzierten * unsigned int-Werte a und b. * * @param a Pointer auf einen unsigned int-Wert (linker Operand) * @param b Pointer auf einen unsigned int-Wert (rechter Operand) * @return -1, falls *a < *b; 1, falls *a > *b; 0, falls *a == *b */ static int compareUInt(const void *a, const void *b) { unsigned int va = *(const unsigned int *)a; unsigned int vb = *(const unsigned int *)b; if (va < vb) return -1; if (va > vb) return 1; return 0; } /** * @brief Vergleichsfunktion für qsort() zur Sortierung von unsigned int-Arrays. * * @param a Pointer auf einen Arrayeintrag * @param b Pointer auf einen Arrayeintrag * @return -1, 0, 1 analog zu compareUInt() */ static int qsort_uint_cmp(const void *a, const void *b) { unsigned int va = *(const unsigned int *)a; unsigned int vb = *(const unsigned int *)b; if (va < vb) return -1; if (va > vb) return 1; return 0; } /** * @brief Erzeugt ein Array aus len Zufallszahlen im Bereich [1 .. 2*len], * das genau einen duplizierten Wert enthält (d. h. len-1 eindeutige + 1 Duplikat), * und mischt die Reihenfolge zufällig. * * Funktionsweise: * - Es werden zunächst len-1 eindeutige Zufallszahlen erzeugt. Die Eindeutigkeit wird * mithilfe eines Binärsuchbaums (BST) geprüft: addToTree() fügt die Zahl ein * und signalisiert per isDup, ob sie bereits vorhanden war. * - Anschließend wird eine der bereits erzeugten Zahlen zufällig ausgewählt und * noch einmal an das Ende des Arrays geschrieben, um das geforderte Duplikat sicherzustellen. * - Zum Schluss wird das gesamte Array mittels Fisher–Yates-Algorithmus gemischt. * * Fehlerbehandlung: * - Bei len < 2 wird NULL zurückgegeben, da das Problem ein Duplikat erfordert. * - Bei Speicher- oder Baum-Insertionsfehlern wird aufgeräumt und NULL zurückgegeben. * Wichtig: Der Baumzeiger root wird erst nach erfolgreichem Insert aktualisiert, * um im Fehlerfall kein bereits aufgebautes Teilbaum-Objekt zu verlieren. * * Randbedingungen / Annahmen: * - addToTree(root, &val, sizeof(val), compareUInt, &isDup) setzt isDup: * isDup == 1 bedeutet „Duplikat gefunden, Baum unverändert“, * isDup == 0 bedeutet „neuer Wert eingefügt (oder Fehler)“. * - Bei Speicherfehler gibt addToTree NULL zurück und isDup bleibt 0. * - clearTree(root) darf mit NULL-Argument aufgerufen werden (No-Op). * * Komplexität: * - Durchschnittlich O(len * log(len)) für die len-1 Einfügungen in den BST. * - Shuffle in O(len). * * @param len Anzahl der zu erzeugenden Werte (muss >= 2 sein) * @return Pointer auf ein Array mit len Einträgen bei Erfolg; NULL bei Fehlern */ unsigned int *createNumbers(unsigned int len) { if (len < 2) return NULL; unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len); if (numbers == NULL) return NULL; // Zufallszahlengenerator nur einmal pro Prozess initialisieren. // Hintergrund: Wird createNumbers mehrfach schnell hintereinander gerufen, // kann time(NULL) identische Seeds liefern und damit identische Zahlenfolgen erzeugen. static int seeded = 0; if (!seeded) { srand((unsigned int)time(NULL)); seeded = 1; } TreeNode *root = NULL; unsigned int range = 2 * len; // Schritt 1: len-1 eindeutige Zufallszahlen erzeugen for (unsigned int i = 0; i < len - 1; i++) { unsigned int val; int isDup; // Wiederholen, bis eine wirklich neue Zahl eingefügt wurde for (;;) { isDup = 0; // vor jedem Insert zurücksetzen, um „alte“ Werte zu vermeiden val = (unsigned int)(rand() % range) + 1; // Wertebereich [1 .. 2*len] // addToTree kann bei Erfolg einen (ggf. neuen) Wurzelzeiger liefern. // Zur Vermeidung eines Speicherlecks bei Fehlern zunächst in temp speichern. TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup); if (newRoot == NULL && isDup == 0) { // Vermutlich Speicher-/Insertionsfehler: aufräumen und abbrechen free(numbers); clearTree(root); // root zeigt noch auf den gültigen Teilbaum return NULL; } if (!isDup) { // Einfügen war erfolgreich und der Wert ist eindeutig. root = newRoot; numbers[i] = val; break; } // Andernfalls Duplikat: Neue Zufallszahl versuchen. } } // Schritt 2: Eine der bestehenden Zahlen zufällig duplizieren unsigned int idx = (unsigned int)(rand() % (len - 1)); // Index im Bereich [0 .. len-2] numbers[len - 1] = numbers[idx]; // Schritt 3: Fisher–Yates-Shuffle über das gesamte Array for (unsigned int i = len - 1; i > 0; i--) { unsigned int j = (unsigned int)(rand() % (i + 1)); unsigned int tmp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = tmp; } // Aufräumen: Baum freigeben clearTree(root); return numbers; } /** * @brief Findet den einzigen duplizierten Wert in einem Array aus len unsigned int. * * Funktionsweise: * - Es wird eine Kopie des Eingabearrays erstellt, um die Reihenfolge des * Originalarrays nicht zu verändern. * - Die Kopie wird mittels qsort() aufsteigend sortiert. * - Beim Durchlauf werden benachbarte Elemente verglichen. Da genau ein Wert * doppelt vorkommt, finden wir ihn als erstes Paar gleicher Nachbarn. * * Fehlerbehandlung: * - Bei ungültigen Parametern (numbers == NULL oder len < 2) wird 0 geliefert. * - Bei Speicherfehlern beim Kopieren ebenfalls 0. * * Komplexität: * - Sortieren in O(len * log(len)), anschließender Linearpass O(len). * * @param numbers Pointer auf das Eingabearray * @param len Länge des Arrays (muss >= 2 sein) * @return Der doppelte Wert; 0 bei Fehlern oder falls kein Duplikat gefunden wurde * (gemäß Aufgabenstellung sollte aber genau ein Duplikat existieren). */ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { if (numbers == NULL || len < 2) return 0; // Kopie erstellen, damit das Original unangetastet bleibt unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); if (copy == NULL) return 0; memcpy(copy, numbers, sizeof(unsigned int) * len); // Sortieren der Kopie qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp); // Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat unsigned int result = 0; for (unsigned int i = 0; i + 1 < len; i++) { if (copy[i] == copy[i + 1]) { result = copy[i]; break; } } free(copy); return result; }