Final
This commit is contained in:
parent
d56c77d77a
commit
ed5f4eedc8
66
bintree.c
66
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);
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
Kilian;9927
|
||||
Kilian;9915
|
||||
Kilian;4975
|
||||
player1;3999
|
||||
|
||||
@ -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");
|
||||
|
||||
BIN
test_numbers.exe
BIN
test_numbers.exe
Binary file not shown.
BIN
test_numbers.o
BIN
test_numbers.o
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user