#include #include "stack.h" #include "bintree.h" // Global static stack used for in-order tree traversal in nextTreeData() // This is needed because nextTreeData() follows strtok-like usage where consecutive calls // with NULL argument continue the iteration static StackNode *traversalStack = NULL; // 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). // Parameters: // - root: pointer to the root of the tree (can be NULL for first insertion) // - data: pointer to the data to be inserted // - dataSize: size in bytes of the data to allocate and copy // - compareFct: comparison function that returns <0 if arg10 if arg1>arg2 // - isDuplicate: pointer to int flag (NULL to allow duplicates, non-NULL to detect and reject duplicates) // Returns: pointer to the root of the modified tree TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { // Base case: if root is NULL, we've found the insertion point // Create a new tree node here if (root == NULL) { // Allocate memory for the new tree node TreeNode *newNode = (TreeNode *)malloc(sizeof(TreeNode)); if (newNode == NULL) { return NULL; // Allocation failed } // Allocate memory for a copy of the data newNode->data = malloc(dataSize); if (newNode->data == NULL) { free(newNode); // Free the node if data allocation failed return NULL; } // Copy the data into the newly allocated space memcpy(newNode->data, data, dataSize); // Initialize the left and right child pointers to NULL newNode->left = NULL; newNode->right = NULL; // If isDuplicate is not NULL, set it to 0 to indicate a new entry was added if (isDuplicate != NULL) { *isDuplicate = 0; } // Return the newly created node return newNode; } // Recursive case: compare the data with the root's data to determine where to insert int comparisonResult = compareFct(data, root->data); // If data is less than root->data, insert into the left subtree if (comparisonResult < 0) { root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); } // If data is greater than root->data, insert into the right subtree else if (comparisonResult > 0) { root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } // If data equals root->data, we have a duplicate else { // If isDuplicate is not NULL, set it to 1 to indicate a duplicate was found if (isDuplicate != NULL) { *isDuplicate = 1; } // If isDuplicate is NULL, we allow the duplicate to be inserted again (in either subtree) // For standard BST behavior, we typically insert duplicates in the right subtree else { root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } } // Return the root (possibly with new subtrees added) return root; } // Iterates over the tree given by root in in-order traversal (Left, Root, Right). // Follows the usage pattern of strtok: first call with root pointer, subsequent calls with NULL to get next element. // Uses a static stack to maintain traversal state between calls. // Parameters: // - root: pointer to tree root for first call, NULL for subsequent calls // Returns: pointer to the data of the next node in in-order sequence, or NULL when traversal is complete void *nextTreeData(TreeNode *root) { // If root is not NULL, we're starting a new traversal if (root != NULL) { // Clear any existing traversal state clearStack(traversalStack); traversalStack = NULL; // Initialize the traversal by pushing root and all its left descendants // In in-order traversal, we always visit left children first TreeNode *current = root; while (current != NULL) { // Push current node onto the stack traversalStack = push(traversalStack, current); // Move to the left child current = current->left; } } // If the traversal stack is empty, we've visited all nodes if (traversalStack == NULL) { return NULL; } // Pop the next node from the stack TreeNode *nextNode = (TreeNode *)top(traversalStack); traversalStack = pop(traversalStack); // If the popped node has a right child, push it and all its left descendants // This handles the "Root, Right" part of in-order traversal if (nextNode->right != NULL) { TreeNode *current = nextNode->right; while (current != NULL) { // Push the current node traversalStack = push(traversalStack, current); // Move to the left child of the right subtree current = current->left; } } // Return the data of the node we just processed return nextNode->data; } // Releases all memory resources used by the tree. // Recursively frees all nodes and their data copies. // Parameters: // - root: pointer to the root of the tree to clear // Returns: void void clearTree(TreeNode *root) { // Base case: if root is NULL, there's nothing to free if (root == NULL) { return; } // Recursive case: first recursively clear the left subtree clearTree(root->left); // Then recursively clear the right subtree clearTree(root->right); // Finally, free the data and the node itself free(root->data); free(root); } // Returns the number of entries (nodes) in the tree. // Recursively counts nodes in the left and right subtrees. // Parameters: // - root: pointer to the root of the tree // Returns: unsigned int representing the total number of nodes in the tree unsigned int treeSize(const TreeNode *root) { // Base case: if root is NULL, there are no nodes if (root == NULL) { return 0; } // Recursive case: count 1 for the current node, plus all nodes in left and right subtrees return 1 + treeSize(root->left) + treeSize(root->right); }