#include #include #include "bintree.h" #include "stack.h" /* Fügt eine Kopie der Daten in den Baum ein, geordnet nach compareFct. Akzeptiert Duplikate, wenn isDuplicate NULL ist, andernfalls ignoriert Duplikate und setzt isDuplicate auf 1 (oder auf 0 bei neuem Eintrag). */ TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { // Überprüfe ungültige Eingabeparameter if (compareFct == NULL || data == NULL || dataSize == 0) return root; // ungültige Eingabe: nichts tun // Wenn der Baum leer ist, erstelle einen neuen Wurzelknoten if (root == NULL) { TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode)); if (node == NULL) return NULL; // Speicherallokation fehlgeschlagen node->data = malloc(dataSize); if (node->data == NULL) { free(node); return NULL; } memcpy(node->data, data, dataSize); node->left = NULL; node->right = NULL; if (isDuplicate != NULL) *isDuplicate = 0; return node; } // Vergleiche neue Daten mit aktueller Wurzel int cmp = compareFct(data, root->data); // Wenn neue Daten kleiner sind, füge in linken Unterbaum ein if (cmp < 0) { root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); } // Wenn neue Daten größer sind, füge in rechten Unterbaum ein else if (cmp > 0) { root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } // Wenn gleich (Duplikat) else { // Wenn Duplikate erkannt werden sollen, setze Flag und ignoriere if (isDuplicate != NULL) { *isDuplicate = 1; } // Andernfalls erlaube Duplikate durch Einfügen in rechten Unterbaum else { root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } } return root; } /* Iteriert über den Baum in aufsteigender Reihenfolge (in-order). Verwendet die Logik von strtok: Wenn root != NULL, initialisiere/reset Iterator für diesen Baum. Wenn root == NULL, setze Iteration von letzter Position fort. Verwendet Stack zur Verwaltung des Traversierungs-Zustands. */ void *nextTreeData(TreeNode *root) { // Statischer Stack zur Aufrechterhaltung des Iterator-Zustands zwischen Aufrufen static StackNode *iterStack = NULL; // Wenn ein neuer Baum bereitgestellt wird, initialisiere den Iterator if (root != NULL) { // Lösche vorherigen Iterator-Zustand clearStack(iterStack); iterStack = NULL; // Pushe die Wurzel und alle linken Nachfahren auf den Stack TreeNode *cur = root; while (cur != NULL) { iterStack = push(iterStack, cur); cur = cur->left; } } else { // Wenn Iteration fortgesetzt wird, aber kein Stack initialisiert, gib NULL zurück if (iterStack == NULL) return NULL; } // Wenn Stack leer ist, keine weiteren Elemente if (iterStack == NULL) return NULL; // Poppe den nächsten Knoten vom Stack (in-order-Traversierung) TreeNode *node = (TreeNode *)top(iterStack); iterStack = pop(iterStack); // Pushe den rechten Unterbaum des aktuellen Knotens und seine linken Nachfahren TreeNode *r = node->right; while (r != NULL) { iterStack = push(iterStack, r); r = r->left; } return node->data; } /* Gibt alle Speicherressourcen frei (einschließlich Datenkopien). */ void clearTree(TreeNode *root) { // Basisfall: wenn Baum leer, nichts tun if (root == NULL) return; // Rekursiv linken und rechten Unterbaum löschen if (root->left != NULL) clearTree(root->left); if (root->right != NULL) clearTree(root->right); // Daten und Knoten selbst freigeben free(root->data); root->data = NULL; free(root); } /* Gibt die Anzahl der Einträge im Baum zurück. */ unsigned int treeSize(const TreeNode *root) { // Basisfall: leerer Baum hat Größe 0 if (root == NULL) return 0; // Größe ist 1 (aktueller Knoten) plus Größen der Unterbäume return 1 + treeSize(root->left) + treeSize(root->right); }