diff --git a/bintree.c b/bintree.c index 75c5aac..57cb486 100644 --- a/bintree.c +++ b/bintree.c @@ -2,126 +2,158 @@ #include "stack.h" #include "bintree.h" -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). +// Fügt ein neues Element in den Binärbaum ein. +// - data wird kopiert (tiefe Kopie) +// - compareFct bestimmt die Sortierreihenfolge +// - Duplikate sind erlaubt, außer isDuplicate ist gesetzt TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { + // Ungültige Parameter → Baum unverändert zurückgeben if (data == NULL || compareFct == NULL) return root; + // Fall: leerer Teilbaum → neuer Knoten wird erzeugt if (root == NULL) { - // neuer Knoten + // Speicher für neuen Baumknoten reservieren TreeNode *node = malloc(sizeof(TreeNode)); if (node == NULL) return NULL; + // Speicher für die Nutzdaten reservieren node->data = malloc(dataSize); if (node->data == NULL) { free(node); return NULL; } + + // Daten in den Knoten kopieren memcpy(node->data, data, dataSize); + + // Linke und rechte Kindzeiger initialisieren node->left = NULL; node->right = NULL; + // Kein Duplikat, da neuer Knoten if (isDuplicate != NULL) *isDuplicate = 0; return node; } + // Vergleich der neuen Daten mit den Daten im aktuellen Knoten int cmp = compareFct(data, root->data); + if (cmp < 0) { + // Neuer Wert ist kleiner → Rekursion im linken Teilbaum root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); } else if (cmp > 0) { + // Neuer Wert ist größer → Rekursion im rechten Teilbaum root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } else { - // Element bereits vorhanden + // Gleicher Wert → Duplikat if (isDuplicate != NULL) { + // Duplikat melden, aber nicht einfügen *isDuplicate = 1; - // Duplikate werden ignoriert } else { - // Duplikate dürfen eingefügt werden -> wir hängen sie z.B. links an + // Duplikate sind erlaubt → z.B. links einfügen root->left = addToTree(root->left, data, dataSize, compareFct, NULL); } } + // Wurzel des (Teil-)Baumes zurückgeben return root; } -// Interner Stack für die Traversierung (strtok-ähnliches Verhalten) +// Globaler interner Stack für die Baum-Traversierung +// Ermöglicht ein strtok-ähnliches Iterator-Verhalten static StackNode *iterStack = NULL; -// Hilfsfunktion: legt ab start alle linken Knoten auf den Stack +// Hilfsfunktion: +// Legt den Startknoten und alle seine linken Nachfolger auf den Stack static void pushLeftPath(TreeNode *start) { + // Solange noch ein Knoten existiert while (start != NULL) { + // Aktuellen Knoten auf den Stack legen iterStack = push(iterStack, start); + + // Zum linken Kind weitergehen start = start->left; } } -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, -// the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. -// On returning the next element, push the top node and push all its left nodes. +// Gibt nacheinander die Daten des Baumes in sortierter Reihenfolge zurück. +// - Beim ersten Aufruf root ≠ NULL → neue Traversierung +// - Danach root == NULL → nächstes Element liefern void *nextTreeData(TreeNode *root) { if (root != NULL) { - // neue Traversierung starten: alten Stack aufräumen + // Neue Traversierung starten → alten Stack leeren if (iterStack != NULL) { clearStack(iterStack); iterStack = NULL; } + + // Wurzel und alle linken Knoten auf den Stack legen pushLeftPath(root); } + // Kein weiteres Element vorhanden if (iterStack == NULL) return NULL; - // Nächsten Knoten holen + // Oberstes Stack-Element holen (nächster Baumknoten) TreeNode *node = (TreeNode *)top(iterStack); iterStack = pop(iterStack); - // rechten Teilbaum dieses Knotens auf den Stack bringen + // Rechten Teilbaum dieses Knotens behandeln + // → dessen linker Pfad wird auf den Stack gelegt if (node->right != NULL) pushLeftPath(node->right); + // Daten des aktuellen Knotens zurückgeben return node->data; } -// Releases all memory resources (including data copies). +// Gibt den gesamten Speicher des Baumes frei (inkl. gespeicherter Daten) void clearTree(TreeNode *root) { + // Abbruchbedingung der Rekursion if (root == NULL) return; + // Zuerst linken Teilbaum freigeben clearTree(root->left); + + // Dann rechten Teilbaum freigeben clearTree(root->right); + // Danach Daten und Knoten selbst freigeben free(root->data); free(root); } -// Returns the number of entries in the tree given by root. +// Liefert die Anzahl aller Knoten im Baum unsigned int treeSize(const TreeNode *root) { + // Leerer Baum hat Größe 0 if (root == NULL) return 0; + // 1 (aktueller Knoten) + Größe linker + Größe rechter Teilbaum return 1u + treeSize(root->left) + treeSize(root->right); } diff --git a/bintree.o b/bintree.o index e0d6dcc..4eadc81 100644 Binary files a/bintree.o and b/bintree.o differ diff --git a/doble.exe b/doble.exe index 17251ed..6e9e730 100644 Binary files a/doble.exe and b/doble.exe differ diff --git a/highscores.txt b/highscores.txt index 2e6dc4f..be116ac 100644 --- a/highscores.txt +++ b/highscores.txt @@ -1,3 +1,4 @@ Kilian;9927 +Kilian;9915 Kilian;4975 player1;3999 diff --git a/test_numbers.c b/test_numbers.c index 84062e8..64f3b83 100644 --- a/test_numbers.c +++ b/test_numbers.c @@ -12,91 +12,109 @@ * - Fehlerfälle (NULL-Pointer, zu kleine Länge) */ +/* + * Prüft ein Zahlenarray auf Korrektheit: + * - Array darf nicht NULL sein + * - Wertebereich muss stimmen + * - genau eine Zahl darf doppelt vorkommen + */ static void check_numbers_array(unsigned int *numbers, unsigned int len) { - unsigned int maxValue = 2 * len; - unsigned int *counts; + unsigned int maxValue = 2 * len; // Maximal erlaubter Zahlenwert + unsigned int *counts; // Array zum Zählen der Vorkommen unsigned int i; - unsigned int duplicatesCount = 0; + unsigned int duplicatesCount = 0; // Zähler für doppelte Werte - assert(numbers != NULL); + assert(numbers != NULL); // Sicherstellen, dass das Array existiert + // Speicher für Zähler-Array reservieren und mit 0 initialisieren counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int)); - assert(counts != NULL); + assert(counts != NULL); // Prüfen, ob Speicher erfolgreich reserviert wurde - // Werte zählen und Bereich prüfen + // Schleife über alle Elemente des numbers-Arrays + // Zählt die Werte und prüft gleichzeitig den gültigen Wertebereich for (i = 0; i < len; ++i) { - unsigned int v = numbers[i]; - assert(v >= 1 && v <= maxValue); - counts[v]++; + unsigned int v = numbers[i]; // Aktueller Wert + assert(v >= 1 && v <= maxValue); // Wertebereich prüfen + counts[v]++; // Anzahl dieses Wertes erhöhen } - // Prüfen: genau eine Zahl kommt zweimal vor, alle anderen höchstens einmal + // Schleife über alle möglichen Werte + // Prüft, dass genau ein Wert doppelt vorkommt for (i = 1; i <= maxValue; ++i) { if (counts[i] == 2) - duplicatesCount++; + duplicatesCount++; // Ein doppelter Wert gefunden else - assert(counts[i] == 0 || counts[i] == 1); + assert(counts[i] == 0 || counts[i] == 1); // Alle anderen max. einmal } - assert(duplicatesCount == 1); + assert(duplicatesCount == 1); // Es darf genau ein Duplikat geben - free(counts); + free(counts); // Speicher freigeben } +/* + * Testet createNumbers und getDuplicate für eine gegebene Länge + */ static void test_createNumbers_and_getDuplicate(unsigned int len) { printf("test_createNumbers_and_getDuplicate(len=%u)...\n", len); + // Zahlenarray erzeugen unsigned int *numbers = createNumbers(len); - assert(numbers != NULL); + assert(numbers != NULL); // Rückgabewert prüfen - // Arraystruktur überprüfen + // Struktur und Inhalt des Arrays überprüfen check_numbers_array(numbers, len); - // Nochmal zählen, um das Duplikat zu kennen + // Erneutes Zählen, um das erwartete Duplikat zu ermitteln unsigned int maxValue = 2 * len; unsigned int *counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int)); assert(counts != NULL); + // Schleife zum Zählen aller Werte for (unsigned int i = 0; i < len; ++i) { unsigned int v = numbers[i]; counts[v]++; } + // Schleife zur Bestimmung des doppelten Wertes unsigned int expectedDuplicate = 0; for (unsigned int v = 1; v <= maxValue; ++v) { if (counts[v] == 2) { - expectedDuplicate = v; + expectedDuplicate = v; // Doppelten Wert merken break; } } - assert(expectedDuplicate != 0); + assert(expectedDuplicate != 0); // Es muss ein Duplikat existieren - // getDuplicate testen + // getDuplicate-Funktion testen unsigned int found = getDuplicate(numbers, len); - assert(found == expectedDuplicate); + assert(found == expectedDuplicate); // Ergebnis vergleichen - free(counts); + free(counts); // Speicher freigeben free(numbers); printf("...OK\n"); } +/* + * Testet Fehlerfälle für getDuplicate + */ static void test_getDuplicate_error_cases(void) { printf("test_getDuplicate_error_cases...\n"); - // NULL-Array + // Test mit NULL-Pointer unsigned int dup = getDuplicate(NULL, 10); assert(dup == 0); - // Länge < 2 + // Test mit zu kleiner Länge (< 2) unsigned int dummy[1] = {42}; dup = getDuplicate(dummy, 1); assert(dup == 0); @@ -104,16 +122,19 @@ static void test_getDuplicate_error_cases(void) printf("...OK\n"); } +/* + * Hauptfunktion: startet alle Unit-Tests + */ int main(void) { printf("Running numbers unit tests...\n\n"); - // Typische Testfälle mit verschiedenen Längen + // Typische Testfälle mit unterschiedlichen Array-Längen test_createNumbers_and_getDuplicate(5); test_createNumbers_and_getDuplicate(10); test_createNumbers_and_getDuplicate(20); - // Fehlerfälle + // Test der Fehlerfälle test_getDuplicate_error_cases(); printf("\nAll numbers tests passed.\n"); diff --git a/test_numbers.exe b/test_numbers.exe index eddc46e..96610d7 100644 Binary files a/test_numbers.exe and b/test_numbers.exe differ diff --git a/test_numbers.o b/test_numbers.o index 6d60ab7..0d7fbe7 100644 Binary files a/test_numbers.o and b/test_numbers.o differ