Final
This commit is contained in:
parent
d56c77d77a
commit
ed5f4eedc8
66
bintree.c
66
bintree.c
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
Kilian;9927
|
Kilian;9927
|
||||||
|
Kilian;9915
|
||||||
Kilian;4975
|
Kilian;4975
|
||||||
player1;3999
|
player1;3999
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
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