info2aufgabe3/bintree.c
shobayoeniolasi99076 a81141fb06 Sortierung+Binärbaum
2026-05-31 09:41:27 +02:00

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);
}