Test numbers.c

This commit is contained in:
z004x46y 2025-12-12 08:28:09 +01:00
parent 4741345749
commit 782525c6ce

199
numbers_test2.c Normal file
View File

@ -0,0 +1,199 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#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 FisherYates-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: FisherYates-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;
}