7 Commits
14 changed files with 234 additions and 80 deletions

No files matched your search

+7 -2
View File
@@ -93,6 +93,11 @@ TreeNode *addToTree(TreeNode *wurzel,
return wurzel; return wurzel;
} }
/*
Die Funktion gibt bei jedem Aufruf das nächste Element des Binärbaums in **Inorder-Reihenfolge**
zurück und merkt sich intern, wo sie zuletzt war.
*/
void *nextTreeData(TreeNode *wurzel) void *nextTreeData(TreeNode *wurzel)
{ {
static StackNode *iteratorStack = NULL; // interner Zustand über Aufrufe hinweg static StackNode *iteratorStack = NULL; // interner Zustand über Aufrufe hinweg
@@ -110,8 +115,8 @@ void *nextTreeData(TreeNode *wurzel)
return NULL; return NULL;
// Nächsten Knoten holen (oberstes Stack-Element) // Nächsten Knoten holen (oberstes Stack-Element)
TreeNode *aktuellerKnoten = (TreeNode *)top(iteratorStack); TreeNode *aktuellerKnoten = (TreeNode *)top(iteratorStack); //Oberstes Element zurück
iteratorStack = pop(iteratorStack); iteratorStack = pop(iteratorStack); // entfernt Oberstes Element
// Falls rechter Teilbaum existiert: dessen linke Kette ablegen // Falls rechter Teilbaum existiert: dessen linke Kette ablegen
if (aktuellerKnoten->right != NULL) if (aktuellerKnoten->right != NULL)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,5 @@
Player1;19887 Player1;19887
Player1;19843
Lena;19811 Lena;19811
Lena;19702 Lena;19702
Player1;19578 Player1;19578
@@ -7,4 +8,3 @@ Lena;9980
Lena;9978 Lena;9978
Lena;9978 Lena;9978
Lena;9976 Lena;9976
Lena;9975
BIN
View File
Binary file not shown.
+126 -38
View File
@@ -1,3 +1,4 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
@@ -5,70 +6,135 @@
#include "numbers.h" #include "numbers.h"
#include "bintree.h" #include "bintree.h"
// helper comparator for unsigned int for bintree /**
* @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) static int compareUInt(const void *a, const void *b)
{ {
unsigned int va = *(const unsigned int *)a; unsigned int va = *(const unsigned int *)a;
unsigned int vb = *(const unsigned int *)b; unsigned int vb = *(const unsigned int *)b;
if(va < vb) return -1; if (va < vb) return -1;
if(va > vb) return 1; if (va > vb) return 1;
return 0; return 0;
} }
// comparator for qsort (unsigned int) /**
* @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) static int qsort_uint_cmp(const void *a, const void *b)
{ {
unsigned int va = *(const unsigned int *)a; unsigned int va = *(const unsigned int *)a;
unsigned int vb = *(const unsigned int *)b; unsigned int vb = *(const unsigned int *)b;
if(va < vb) return -1; if (va < vb) return -1;
if(va > vb) return 1; if (va > vb) return 1;
return 0; return 0;
} }
// Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries. /**
// Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while * @brief Erzeugt ein Array aus len Zufallszahlen im Bereich [1 .. 2*len],
// creating random numbers. * 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) unsigned int *createNumbers(unsigned int len)
{ {
if(len < 2) if (len < 2)
return NULL; return NULL;
unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len); unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len);
if(numbers == NULL) if (numbers == NULL)
return NULL; return NULL;
// seed once // Zufallszahlengenerator nur einmal pro Prozess initialisieren.
srand((unsigned int)time(NULL)); // 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; TreeNode *root = NULL;
unsigned int range = 2 * len; unsigned int range = 2 * len;
// create len-1 unique numbers
for(unsigned int i = 0; i < len - 1; i++) // Schritt 1: len-1 eindeutige Zufallszahlen erzeugen
for (unsigned int i = 0; i < len - 1; i++)
{ {
unsigned int val; unsigned int val;
int isDup = 0; int isDup;
// try until a unique number is inserted
do // Wiederholen, bis eine wirklich neue Zahl eingefügt wurde
for (;;)
{ {
val = (unsigned int)(rand() % range) + 1; // [1..2*len] isDup = 0; // vor jedem Insert zurücksetzen, um „alte“ Werte zu vermeiden
root = addToTree(root, &val, sizeof(val), compareUInt, &isDup); val = (unsigned int)(rand() % range) + 1; // Wertebereich [1 .. 2*len]
// if addToTree returned NULL due to allocation failure, cleanup and return NULL
if(root == NULL && isDup == 0) // 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); free(numbers);
clearTree(root); clearTree(root); // root zeigt noch auf den gültigen Teilbaum
return NULL; return NULL;
} }
} while(isDup);
numbers[i] = val; if (!isDup) {
// Einfügen war erfolgreich und der Wert ist eindeutig.
root = newRoot;
numbers[i] = val;
break;
}
// Andernfalls Duplikat: Neue Zufallszahl versuchen.
}
} }
// duplicate one existing random entry // Schritt 2: Eine der bestehenden Zahlen zufällig duplizieren
unsigned int idx = (unsigned int)(rand() % (len - 1)); unsigned int idx = (unsigned int)(rand() % (len - 1)); // Index im Bereich [0 .. len-2]
numbers[len - 1] = numbers[idx]; numbers[len - 1] = numbers[idx];
// shuffle array (Fisher-Yates) // Schritt 3: Fisher–Yates-Shuffle über das gesamte Array
for(unsigned int i = len - 1; i > 0; i--) for (unsigned int i = len - 1; i > 0; i--)
{ {
unsigned int j = (unsigned int)(rand() % (i + 1)); unsigned int j = (unsigned int)(rand() % (i + 1));
unsigned int tmp = numbers[i]; unsigned int tmp = numbers[i];
@@ -76,31 +142,53 @@ unsigned int *createNumbers(unsigned int len)
numbers[j] = tmp; numbers[j] = tmp;
} }
// free tree resources // Aufräumen: Baum freigeben
clearTree(root); clearTree(root);
return numbers; return numbers;
} }
// Returns only the only number in numbers which is present twice. Returns zero on errors. /**
* @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) unsigned int getDuplicate(const unsigned int numbers[], unsigned int len)
{ {
if(numbers == NULL || len < 2) if (numbers == NULL || len < 2)
return 0; return 0;
// make a copy so original array order is not modified by caller expectation // Kopie erstellen, damit das Original unangetastet bleibt
unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len);
if(copy == NULL) if (copy == NULL)
return 0; return 0;
memcpy(copy, numbers, sizeof(unsigned int) * len); memcpy(copy, numbers, sizeof(unsigned int) * len);
// Sortieren der Kopie
qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp); qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp);
// Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat
unsigned int result = 0; unsigned int result = 0;
for(unsigned int i = 0; i + 1 < len; i++) for (unsigned int i = 0; i + 1 < len; i++)
{ {
if(copy[i] == copy[i+1]) if (copy[i] == copy[i + 1]) {
{
result = copy[i]; result = copy[i];
break; break;
} }
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -4,12 +4,12 @@
// Push Daten auf den Stack legen. // Push Daten auf den Stack legen.
StackNode *push(StackNode *stack, void *data) StackNode *push(StackNode *stack, void *data)
{ {
StackNode *node = (StackNode *)malloc(sizeof(StackNode)); StackNode *node = malloc(sizeof(StackNode));
if(node == NULL) if(node == NULL)
return stack; // allocation failed -> return unchanged stack return stack; // allocation failed -> return unchanged stack
node->data = data; // Set the data for the new node node->data = data; // Setze Daten
node->next = stack; // New node points to the previous top of the stack node->next = stack; // Setze nächsten Knoten auf aktuellen Stack
return node; return node;
} }
@@ -21,7 +21,7 @@ StackNode *pop(StackNode *stack)
return NULL; return NULL;
StackNode *next = stack->next; StackNode *next = stack->next;
// Do NOT free stack->data here; caller owns the pointed data // Speicher des aktuellen Knotens freigeben
free(stack); free(stack);
return next; return next;
} }
BIN
View File
Binary file not shown.
+8 -5
View File
@@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include "bintree.h" #include "bintree.h"
// comparator for unsigned int values stored by value in heap // Vergleichsfunktion für unsigned int
static int cmp_uint_ptr(const void *a, const void *b) static int cmp_uint_ptr(const void *a, const void *b)
{ {
unsigned int va = *(const unsigned int *)a; unsigned int va = *(const unsigned int *)a;
@@ -19,7 +19,7 @@ int main(void)
unsigned int vals[] = {5, 2, 8, 1, 3, 7, 9}; unsigned int vals[] = {5, 2, 8, 1, 3, 7, 9};
const size_t n = sizeof(vals)/sizeof(vals[0]); const size_t n = sizeof(vals)/sizeof(vals[0]);
// insert values (copy made by addToTree) // Werte in den Baum einfügen
for(size_t i = 0; i < n; i++) for(size_t i = 0; i < n; i++)
{ {
int isDup = 0; int isDup = 0;
@@ -27,22 +27,25 @@ int main(void)
if(root == NULL && isDup == 0) { fprintf(stderr, "addToTree allocation failed\n"); return 1; } if(root == NULL && isDup == 0) { fprintf(stderr, "addToTree allocation failed\n"); return 1; }
} }
// Baumgröße prüfen
unsigned int sz = treeSize(root); unsigned int sz = treeSize(root);
if(sz != n) { fprintf(stderr, "treeSize expected %zu got %u\n", n, sz); clearTree(root); return 2; } if(sz != n) { fprintf(stderr, "treeSize expected %zu got %u\n", n, sz); clearTree(root); return 2; }
// inorder traversal using nextTreeData // Inorder-Traversierung (muss sortiert sein)
unsigned int last = 0; unsigned int last = 0;
int first = 1; int first = 1;
unsigned int *data = nextTreeData(root); unsigned int *data = nextTreeData(root);
while(data != NULL) while(data != NULL)
{ {
unsigned int v = *data; unsigned int v = *data;
// Prüfen, ob Reihenfolge korrekt ist
if(!first && v < last) { fprintf(stderr, "inorder traversal not sorted: %u after %u\n", v, last); clearTree(root); return 3; } if(!first && v < last) { fprintf(stderr, "inorder traversal not sorted: %u after %u\n", v, last); clearTree(root); return 3; }
last = v; first = 0; last = v; first = 0;
data = nextTreeData(NULL); data = nextTreeData(NULL); // nächstes Element holen
} }
clearTree(root); clearTree(root); // Speicher freigeben
printf("test_bintree: OK\n"); printf("test_bintree: OK\n");
return 0; return 0;
} }
+69 -19
View File
@@ -1,41 +1,91 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "numbers.h" #include "numbers.h"
/**
* @brief Selbsttest für createNumbers() und getDuplicate().
*
* Erzeugt ein Array aus len Zufallszahlen mit genau einem duplizierten Wert,
* validiert die Eigenschaften per Zähl-Array (Wertebereich, Häufigkeiten)
* und prüft anschließend, ob getDuplicate() dasselbe Duplikat ermittelt.
*
* Rückgabecodes:
* 0: OK
* 1: createNumbers() fehlgeschlagen
* 2: Speicherfehler für counts
* 3: Wert außerhalb des Bereichs [1..2*len]
* 4: Ein Wert erscheint öfter als zweimal
* 5: Nicht genau ein Duplikat gefunden
* 6: getDuplicate() liefert anderes Ergebnis als Zählung
*/
int main(void) int main(void)
{ {
unsigned int len = 100; unsigned int len = 100; // Anzahl zu erzeugender Zahlen
unsigned int *nums = createNumbers(len); unsigned int *nums = createNumbers(len);
if(nums == NULL) { fprintf(stderr, "createNumbers returned NULL\n"); return 1; } if (nums == NULL) {
fprintf(stderr, "createNumbers returned NULL\n");
// count occurrences return 1; // Erzeugung fehlgeschlagen
unsigned int maxVal = 2 * len;
unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int));
if(counts == NULL) { free(nums); return 2; }
for(unsigned int i = 0; i < len; i++)
{
if(nums[i] > maxVal) { fprintf(stderr, "value out of expected range\n"); free(nums); free(counts); return 3; }
counts[nums[i]]++;
} }
unsigned int maxVal = 2 * len; // Erlaubter Bereich: [1 .. 2*len]
// Zähl-Array für Häufigkeiten pro Wert (Index 0 bleibt ungenutzt)
unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int));
if (counts == NULL) {
free(nums);
return 2; // Speicherfehler bei counts
}
// Häufigkeiten bestimmen und gleichzeitig Bereich prüfen
for (unsigned int i = 0; i < len; i++) {
unsigned int v = nums[i];
if (v == 0 || v > maxVal) { // sollte nicht passieren, wenn createNumbers korrekt ist
fprintf(stderr, "value out of expected range\n");
free(nums);
free(counts);
return 3;
}
counts[v]++; // Auftreten des Werts v zählen
}
// Exakt ein Wert muss doppelt vorkommen; keiner darf >2-mal vorkommen
int duplicatesFound = 0; int duplicatesFound = 0;
unsigned int duplicateValue = 0; unsigned int duplicateValue = 0;
for(unsigned int v = 1; v <= maxVal; v++)
{ for (unsigned int v = 1; v <= maxVal; v++) {
if(counts[v] == 2) { duplicatesFound++; duplicateValue = v; } if (counts[v] == 2) {
else if(counts[v] > 2) { fprintf(stderr, "value %u appears more than twice\n", v); free(nums); free(counts); return 4; } duplicatesFound++;
duplicateValue = v; // den doppelten Wert merken
} else if (counts[v] > 2) {
fprintf(stderr, "value %u appears more than twice\n", v);
free(nums);
free(counts);
return 4; // Vertragsbruch: zu häufiges Auftreten
}
// counts[v] == 0 oder 1 sind unkritisch
} }
if(duplicatesFound != 1) { fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound); free(nums); free(counts); return 5; } if (duplicatesFound != 1) {
fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound);
free(nums);
free(counts);
return 5; // zu wenige/zu viele Duplikate
}
// Ergebnis von getDuplicate() mit der Zählung abgleichen
unsigned int found = getDuplicate(nums, len); unsigned int found = getDuplicate(nums, len);
if(found != duplicateValue) { fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue); free(nums); free(counts); return 6; } if (found != duplicateValue) {
fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue);
free(nums);
free(counts);
return 6; // Abweichung zwischen Methoden
}
// Ressourcen freigeben und Erfolg melden
free(nums); free(nums);
free(counts); free(counts);
printf("test_numbers: OK (duplicate = %u)\n", duplicateValue); printf("test_numbers: OK (duplicate = %u)\n", duplicateValue);
return 0; return 0;
} }
+19 -11
View File
@@ -8,37 +8,45 @@ int main(void)
StackNode *s = NULL; StackNode *s = NULL;
// push 3 integers // push 3 integers
int *a = malloc(sizeof(int)); *a = 1; s = push(s, a); int *a = malloc(sizeof(int));
int *b = malloc(sizeof(int)); *b = 2; s = push(s, b); *a = 1;
int *c = malloc(sizeof(int)); *c = 3; s = push(s, c); s = push(s, a);
int *b = malloc(sizeof(int));
*b = 2;
s = push(s, b);
int *c = malloc(sizeof(int));
*c = 3;
s = push(s, c);
// top should be c (3) // oben liegt c (3)
int *topv = (int *)top(s); int *topv = (int *)top(s);
if(topv == NULL || *topv != 3) { fprintf(stderr, "stack top expected 3\n"); return 1; } if(topv == NULL || *topv != 3) { fprintf(stderr, "stack top expected 3\n"); return 1; }
// pop -> now top should be 2 // oben liegt nun 2
s = pop(s); s = pop(s);
topv = (int *)top(s); topv = (int *)top(s);
if(topv == NULL || *topv != 2) { fprintf(stderr, "stack top expected 2 after pop\n"); return 2; } if(topv == NULL || *topv != 2) { fprintf(stderr, "stack top expected 2 after pop\n"); return 2; }
// pop -> now top should be 1 // oben liegt nun 1
s = pop(s); s = pop(s);
topv = (int *)top(s); topv = (int *)top(s);
if(topv == NULL || *topv != 1) { fprintf(stderr, "stack top expected 1 after pop\n"); return 3; } if(topv == NULL || *topv != 1) { fprintf(stderr, "stack top expected 1 after pop\n"); return 3; }
// pop last // letzen Stapelinhalt holen
s = pop(s); s = pop(s);
if(s != NULL) { fprintf(stderr, "stack expected empty after popping all\n"); return 4; } if(s != NULL) { fprintf(stderr, "stack expected empty after popping all\n"); return 4; }
// free stored data (stack does not free data) // Eigenen Speicher freigeben
free(a); free(b); free(c); free(a);
free(b);
free(c);
// test clearStack on empty and small stacks // test clearStack mit leerem Stack
s = push(s, malloc(sizeof(int))); s = push(s, malloc(sizeof(int)));
s = push(s, malloc(sizeof(int))); s = push(s, malloc(sizeof(int)));
clearStack(s); // clearStack must free nodes but not payload; free payloads not necessary because we leaked intentionally for test of API clearStack(s); // clearStack must free nodes but not payload; free payloads not necessary because we leaked intentionally for test of API
// Note: above payloads are not freed (stack API spec); this test ensures clearStack doesn't crash. // Note: above payloads are not freed (stack API spec); this test ensures clearStack doesn't crash.
// Success // Funktioniert alles
printf("test_stack: OK\n"); printf("test_stack: OK\n");
return 0; return 0;
} }
BIN
View File
Binary file not shown.