This commit is contained in:
Kilian Muckelbauer 2025-12-15 21:04:35 +01:00
parent d56c77d77a
commit ed5f4eedc8
7 changed files with 97 additions and 43 deletions

View File

@ -2,126 +2,158 @@
#include "stack.h" #include "stack.h"
#include "bintree.h" #include "bintree.h"
// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates // Fügt ein neues Element in den Binärbaum ein.
// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). // - 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, TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
CompareFctType compareFct, int *isDuplicate) CompareFctType compareFct, int *isDuplicate)
{ {
// Ungültige Parameter → Baum unverändert zurückgeben
if (data == NULL || compareFct == NULL) if (data == NULL || compareFct == NULL)
return root; return root;
// Fall: leerer Teilbaum → neuer Knoten wird erzeugt
if (root == NULL) if (root == NULL)
{ {
// neuer Knoten // Speicher für neuen Baumknoten reservieren
TreeNode *node = malloc(sizeof(TreeNode)); TreeNode *node = malloc(sizeof(TreeNode));
if (node == NULL) if (node == NULL)
return NULL; return NULL;
// Speicher für die Nutzdaten reservieren
node->data = malloc(dataSize); node->data = malloc(dataSize);
if (node->data == NULL) if (node->data == NULL)
{ {
free(node); free(node);
return NULL; return NULL;
} }
// Daten in den Knoten kopieren
memcpy(node->data, data, dataSize); memcpy(node->data, data, dataSize);
// Linke und rechte Kindzeiger initialisieren
node->left = NULL; node->left = NULL;
node->right = NULL; node->right = NULL;
// Kein Duplikat, da neuer Knoten
if (isDuplicate != NULL) if (isDuplicate != NULL)
*isDuplicate = 0; *isDuplicate = 0;
return node; return node;
} }
// Vergleich der neuen Daten mit den Daten im aktuellen Knoten
int cmp = compareFct(data, root->data); int cmp = compareFct(data, root->data);
if (cmp < 0) if (cmp < 0)
{ {
// Neuer Wert ist kleiner → Rekursion im linken Teilbaum
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
} }
else if (cmp > 0) else if (cmp > 0)
{ {
// Neuer Wert ist größer → Rekursion im rechten Teilbaum
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
} }
else else
{ {
// Element bereits vorhanden // Gleicher Wert → Duplikat
if (isDuplicate != NULL) if (isDuplicate != NULL)
{ {
// Duplikat melden, aber nicht einfügen
*isDuplicate = 1; *isDuplicate = 1;
// Duplikate werden ignoriert
} }
else 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); root->left = addToTree(root->left, data, dataSize, compareFct, NULL);
} }
} }
// Wurzel des (Teil-)Baumes zurückgeben
return root; 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; 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) static void pushLeftPath(TreeNode *start)
{ {
// Solange noch ein Knoten existiert
while (start != NULL) while (start != NULL)
{ {
// Aktuellen Knoten auf den Stack legen
iterStack = push(iterStack, start); iterStack = push(iterStack, start);
// Zum linken Kind weitergehen
start = start->left; start = start->left;
} }
} }
// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, // Gibt nacheinander die Daten des Baumes in sortierter Reihenfolge zurück.
// the next entry of the last tree given is returned in ordering direction. // - Beim ersten Aufruf root ≠ NULL → neue Traversierung
// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. // - Danach root == NULL → nächstes Element liefern
// On returning the next element, push the top node and push all its left nodes.
void *nextTreeData(TreeNode *root) void *nextTreeData(TreeNode *root)
{ {
if (root != NULL) if (root != NULL)
{ {
// neue Traversierung starten: alten Stack aufräumen // Neue Traversierung starten → alten Stack leeren
if (iterStack != NULL) if (iterStack != NULL)
{ {
clearStack(iterStack); clearStack(iterStack);
iterStack = NULL; iterStack = NULL;
} }
// Wurzel und alle linken Knoten auf den Stack legen
pushLeftPath(root); pushLeftPath(root);
} }
// Kein weiteres Element vorhanden
if (iterStack == NULL) if (iterStack == NULL)
return NULL; return NULL;
// Nächsten Knoten holen // Oberstes Stack-Element holen (nächster Baumknoten)
TreeNode *node = (TreeNode *)top(iterStack); TreeNode *node = (TreeNode *)top(iterStack);
iterStack = pop(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) if (node->right != NULL)
pushLeftPath(node->right); pushLeftPath(node->right);
// Daten des aktuellen Knotens zurückgeben
return node->data; return node->data;
} }
// Releases all memory resources (including data copies). // Gibt den gesamten Speicher des Baumes frei (inkl. gespeicherter Daten)
void clearTree(TreeNode *root) void clearTree(TreeNode *root)
{ {
// Abbruchbedingung der Rekursion
if (root == NULL) if (root == NULL)
return; return;
// Zuerst linken Teilbaum freigeben
clearTree(root->left); clearTree(root->left);
// Dann rechten Teilbaum freigeben
clearTree(root->right); clearTree(root->right);
// Danach Daten und Knoten selbst freigeben
free(root->data); free(root->data);
free(root); 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) unsigned int treeSize(const TreeNode *root)
{ {
// Leerer Baum hat Größe 0
if (root == NULL) if (root == NULL)
return 0; return 0;
// 1 (aktueller Knoten) + Größe linker + Größe rechter Teilbaum
return 1u + treeSize(root->left) + treeSize(root->right); return 1u + treeSize(root->left) + treeSize(root->right);
} }

BIN
bintree.o

Binary file not shown.

BIN
doble.exe

Binary file not shown.

View File

@ -1,3 +1,4 @@
Kilian;9927 Kilian;9927
Kilian;9915
Kilian;4975 Kilian;4975
player1;3999 player1;3999

View File

@ -12,91 +12,109 @@
* - Fehlerfälle (NULL-Pointer, zu kleine Länge) * - 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) static void check_numbers_array(unsigned int *numbers, unsigned int len)
{ {
unsigned int maxValue = 2 * len; unsigned int maxValue = 2 * len; // Maximal erlaubter Zahlenwert
unsigned int *counts; unsigned int *counts; // Array zum Zählen der Vorkommen
unsigned int i; 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)); 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) for (i = 0; i < len; ++i)
{ {
unsigned int v = numbers[i]; unsigned int v = numbers[i]; // Aktueller Wert
assert(v >= 1 && v <= maxValue); assert(v >= 1 && v <= maxValue); // Wertebereich prüfen
counts[v]++; 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) for (i = 1; i <= maxValue; ++i)
{ {
if (counts[i] == 2) if (counts[i] == 2)
duplicatesCount++; duplicatesCount++; // Ein doppelter Wert gefunden
else 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) static void test_createNumbers_and_getDuplicate(unsigned int len)
{ {
printf("test_createNumbers_and_getDuplicate(len=%u)...\n", len); printf("test_createNumbers_and_getDuplicate(len=%u)...\n", len);
// Zahlenarray erzeugen
unsigned int *numbers = createNumbers(len); 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); 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 maxValue = 2 * len;
unsigned int *counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int)); unsigned int *counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int));
assert(counts != NULL); assert(counts != NULL);
// Schleife zum Zählen aller Werte
for (unsigned int i = 0; i < len; ++i) for (unsigned int i = 0; i < len; ++i)
{ {
unsigned int v = numbers[i]; unsigned int v = numbers[i];
counts[v]++; counts[v]++;
} }
// Schleife zur Bestimmung des doppelten Wertes
unsigned int expectedDuplicate = 0; unsigned int expectedDuplicate = 0;
for (unsigned int v = 1; v <= maxValue; ++v) for (unsigned int v = 1; v <= maxValue; ++v)
{ {
if (counts[v] == 2) if (counts[v] == 2)
{ {
expectedDuplicate = v; expectedDuplicate = v; // Doppelten Wert merken
break; break;
} }
} }
assert(expectedDuplicate != 0); assert(expectedDuplicate != 0); // Es muss ein Duplikat existieren
// getDuplicate testen // getDuplicate-Funktion testen
unsigned int found = getDuplicate(numbers, len); unsigned int found = getDuplicate(numbers, len);
assert(found == expectedDuplicate); assert(found == expectedDuplicate); // Ergebnis vergleichen
free(counts); free(counts); // Speicher freigeben
free(numbers); free(numbers);
printf("...OK\n"); printf("...OK\n");
} }
/*
* Testet Fehlerfälle für getDuplicate
*/
static void test_getDuplicate_error_cases(void) static void test_getDuplicate_error_cases(void)
{ {
printf("test_getDuplicate_error_cases...\n"); printf("test_getDuplicate_error_cases...\n");
// NULL-Array // Test mit NULL-Pointer
unsigned int dup = getDuplicate(NULL, 10); unsigned int dup = getDuplicate(NULL, 10);
assert(dup == 0); assert(dup == 0);
// Länge < 2 // Test mit zu kleiner Länge (< 2)
unsigned int dummy[1] = {42}; unsigned int dummy[1] = {42};
dup = getDuplicate(dummy, 1); dup = getDuplicate(dummy, 1);
assert(dup == 0); assert(dup == 0);
@ -104,16 +122,19 @@ static void test_getDuplicate_error_cases(void)
printf("...OK\n"); printf("...OK\n");
} }
/*
* Hauptfunktion: startet alle Unit-Tests
*/
int main(void) int main(void)
{ {
printf("Running numbers unit tests...\n\n"); 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(5);
test_createNumbers_and_getDuplicate(10); test_createNumbers_and_getDuplicate(10);
test_createNumbers_and_getDuplicate(20); test_createNumbers_and_getDuplicate(20);
// Fehlerfälle // Test der Fehlerfälle
test_getDuplicate_error_cases(); test_getDuplicate_error_cases();
printf("\nAll numbers tests passed.\n"); printf("\nAll numbers tests passed.\n");

Binary file not shown.

Binary file not shown.