184 lines
6.4 KiB
C
184 lines
6.4 KiB
C
#include <string.h>
|
|
#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 arg1<arg2, 0 if equal, >0 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);
|
|
} |