From d10b87ef2b7976a728436d71e2996c07bbc023cc Mon Sep 17 00:00:00 2001 From: Coleman Date: Wed, 2 Jul 2008 23:44:56 -0500 Subject: [PATCH] Importing from busted CVS --- AVLTree.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Element.c | 18 ++++ Element.h | 11 ++ Tree.h | 55 ++++++++++ main.c | 183 ++++++++++++++++++++++++++++++++ 5 files changed, 573 insertions(+) create mode 100644 AVLTree.c create mode 100644 Element.c create mode 100644 Element.h create mode 100644 Tree.h create mode 100644 main.c diff --git a/AVLTree.c b/AVLTree.c new file mode 100644 index 0000000..f792027 --- /dev/null +++ b/AVLTree.c @@ -0,0 +1,306 @@ +/* + * A free AVLTree implementation written by Andrew Coleman + * find out more cool things and licensing at http://penguincoder.org + */ +#include +#include + +#include "Tree.h" + +TreeNode *FindByElement(TreeNode *rootNode, Element Data) +{ + short comp; + TreeNode *Return; + + /* sanity checks */ + if(Data == NULL) + return NULL; + else if(rootNode == NULL || rootNode->Item == NULL) { + /* reached a leaf, and the data was not found */ + return NULL; + } + + /* non recursive search because it really isn't neccessary right here */ + Return = rootNode; + do { + comp = Compare(Return->Item, Data); + if(comp < 0) + Return = Return->Left; + else if(comp > 0) + Return = Return->Right; + } while(Return != NULL && comp != 0); + return Return; +} + +short IsEmpty(TreeNode *rootNode) +{ + return (rootNode == NULL); +} + +TreeNode *SingleRightRotation(TreeNode *rootNode) +{ + TreeNode *newRoot = NULL; + newRoot = rootNode->Left; + rootNode->Left = newRoot->Right; + newRoot->Right = rootNode; + return newRoot; +} + +TreeNode *SingleLeftRotation(TreeNode *rootNode) +{ + TreeNode *newRoot = NULL; + newRoot = rootNode->Right; + rootNode->Right = newRoot->Left; + newRoot->Left = rootNode; + return newRoot; +} + +TreeNode *DoubleRightRotation(TreeNode *rootNode) +{ + TreeNode *newRoot = SingleLeftRotation(rootNode->Left); + rootNode->Left = newRoot; + newRoot = SingleRightRotation(rootNode); + return newRoot; +} + +TreeNode *DoubleLeftRotation(TreeNode *rootNode) +{ + TreeNode *newRoot = SingleRightRotation(rootNode->Right); + rootNode->Right = newRoot; + newRoot = SingleLeftRotation(rootNode); + return newRoot; +} + +TreeNode *CheckTreeNodeRotation(TreeNode *tnode, + TreeNode *subtree, short *finished) +{ + /* this function will check to see if tnode's Balance factor indicates + that a rotation is needed. Subtree is the child link that was either + just added or possibly changed */ + switch(tnode->Balance) { + case 0: + /* nothing special, but we have balanced out properly, yay */ + *finished = 1; + break; + case 2: + /* right heavy */ + *finished = 1; + if(subtree->Balance == -1) { + /* update balance factors */ + if(subtree->Left->Balance == 1) { + subtree->Balance = 0; + tnode->Balance = -1; + } else if(subtree->Left->Balance == -1) { + subtree->Balance = 1; + tnode->Balance = 0; + } else + subtree->Balance = tnode->Balance = 0; + subtree->Left->Balance = 0; + tnode = DoubleLeftRotation(tnode); + } else if(subtree->Balance == 1) { + /* update balance factors */ + subtree->Balance = tnode->Balance = 0; + tnode = SingleLeftRotation(tnode); + } + break; + case -2: + /* left heavy */ + *finished = 1; + if(subtree->Balance == 1) { + /* update balance factors */ + if(subtree->Right->Balance == -1) { + subtree->Balance = 0; + tnode->Balance = 1; + } else if(subtree->Right->Balance == 1) { + subtree->Balance = -1; + tnode->Balance = 0; + } else + subtree->Balance = tnode->Balance = 0; + subtree->Right->Balance = 0; + tnode = DoubleRightRotation(tnode); + } else if(subtree->Balance == -1) { + /* update balance factors */ + subtree->Balance = tnode->Balance = 0; + tnode = SingleRightRotation(tnode); + } + } + return tnode; +} + +TreeNode *InsertNode(TreeNode *tnode, Element Item, + char *Name, short *finished) +{ + TreeNode *subtree = NULL; + short comp = 0; + + if(tnode == NULL) { + /* found the spot that the TreeNode belongs */ + tnode = malloc(sizeof(TreeNode)); + /* malloc problem, return NULL */ + if(tnode == NULL) + return NULL; + else { + /* default settings for a new node */ + tnode->Left = NULL; + tnode->Right = NULL; + tnode->Item = Item; + tnode->Name = Name; + tnode->Balance = 0; + } + } else { + /* need to find the new node, don't check for a 0, + cos its already in teh tree, foo */ + comp = Compare(Item, tnode->Item); + if(comp < 0) { + /* go left */ + subtree = InsertNode(tnode->Left, Item, Name, finished); + /* if this is NULL, there is problem. quit now */ + if(subtree == NULL) + return NULL; + /* you are not finished until Balance == 0, reached the root + or a rotation has occured */ + if(!*finished) + tnode->Balance--; + tnode->Left = subtree; + } else if(comp > 0) { + /* go right */ + subtree = InsertNode(tnode->Right, Item, Name, finished); + if(subtree == NULL) + return NULL; + if(!*finished) + tnode->Balance++; + tnode->Right = subtree; + } else + /* got a dupe, return NULL */ + return NULL; + + /* check if a rotation is neccessary */ + if(!*finished) { + tnode = CheckTreeNodeRotation(tnode, subtree, finished); + } + } + return tnode; +} + +TreeNode *Insert(TreeNode *tnode, Element Item, char *Name) +{ + /* flag for the recursive function */ + short done = 0; + /* run and return the recursive insert */ + return InsertNode(tnode, Item, Name, &done); +} + +void MakeEmpty(TreeNode *rootNode) +{ + /* this function is pretty simple, just traverses the tree and starts + free'ing from the bottom up */ + if(rootNode == NULL) + return; + MakeEmpty(rootNode->Left); + MakeEmpty(rootNode->Right); + FreeElement(rootNode->Item); + free(rootNode->Name); + free(rootNode); +} + +TreeNode *DeleteLeftMost(TreeNode **deletedNode, TreeNode *rootNode, + short *finished) +{ + TreeNode *subtree = rootNode->Right; + if(rootNode == NULL) { + printf("bailing!\n"); + return NULL; + } + if(rootNode->Left == NULL) { + free((*deletedNode)->Name); + FreeElement((*deletedNode)->Item); + (*deletedNode)->Name = rootNode->Name; + (*deletedNode)->Item = rootNode->Item; + free(rootNode); + rootNode = subtree; + } else { + rootNode->Left = DeleteLeftMost(deletedNode, rootNode->Left, finished); + if(!*finished) { + rootNode->Balance++; + if(rootNode->Left != NULL) + rootNode = CheckTreeNodeRotation(rootNode, rootNode->Left, + finished); + } + } + return rootNode; +} + +TreeNode *DeleteByElementRecursive(TreeNode *rootNode, + Element Item, short *finished) +{ + TreeNode *subtree = NULL; + short comp = 0; + + /* found a leaf */ + if(rootNode == NULL) + return NULL; + /* figure out where the node in question is in relation to the current */ + comp = Compare(rootNode->Item, Item); + if(comp > 0) { + /* buried somewhere in the right subtree */ + subtree = DeleteByElementRecursive(rootNode->Right, Item, finished); + rootNode->Right = subtree; + if(!*finished && subtree != NULL) { + rootNode->Balance--; + rootNode = CheckTreeNodeRotation(rootNode, subtree, finished); + } + } else if(comp < 0) { + subtree = DeleteByElementRecursive(rootNode->Left, Item, finished); + rootNode->Left = subtree; + if(!*finished && subtree != NULL) { + rootNode->Balance++; + rootNode = CheckTreeNodeRotation(rootNode, subtree, finished); + } + } else { + /* found what we were looking for */ + if(rootNode->Right == NULL) { + /* rootNode is replaced by the left child; can be null */ + subtree = rootNode->Left; + free(rootNode->Name); + FreeElement(rootNode->Item); + free(rootNode); + rootNode = subtree; + } else if(rootNode->Right->Left == NULL) { + /* rootNode is replaced by Right */ + subtree = rootNode->Right; + subtree->Balance = rootNode->Balance; + subtree->Left = rootNode->Left; + free(rootNode->Name); + FreeElement(rootNode->Item); + free(rootNode); + rootNode = subtree; + } else { + /* rootNode is replaced by inorder successor */ + /* re-using comp as another flag since it is 0 */ + rootNode->Right = DeleteLeftMost(&rootNode, rootNode->Right, &comp); + } + } + return rootNode; +} + +TreeNode *DeleteByElement(TreeNode *rootNode, Element Item) +{ + short finished = 0; + TreeNode *newRoot = NULL; + newRoot = DeleteByElementRecursive(rootNode, Item, &finished); + return newRoot; +} + +long Height(TreeNode *rootNode) +{ + /* recursive function to determine the height by comparing the height of + the left and right child and returning the larger value */ + long lresult = 0, rresult = 0; + /* either the tree is empty or we found a leaf */ + if(rootNode == NULL) + return 0; + lresult = Height(rootNode->Left); + rresult = Height(rootNode->Right); + /* minimum height is 1, but that's probably correct */ + return (lresult > rresult ? lresult : rresult) + 1; +} diff --git a/Element.c b/Element.c new file mode 100644 index 0000000..571277e --- /dev/null +++ b/Element.c @@ -0,0 +1,18 @@ +#include + +#include "Element.h" + +short Compare(Element Data1, Element Data2) +{ + if(*Data1 > *Data2) + return 1; + else if(*Data1 < *Data2) + return -1; + return 0; +} + +void FreeElement(Element Data) +{ + free(Data); +} + diff --git a/Element.h b/Element.h new file mode 100644 index 0000000..44199bc --- /dev/null +++ b/Element.h @@ -0,0 +1,11 @@ +#ifndef __Element_H + +#define __Element_H + +typedef long *Element; + +short Compare(Element Data1, Element Data2); + +void FreeElement(Element Data); + +#endif diff --git a/Tree.h b/Tree.h new file mode 100644 index 0000000..2cb679d --- /dev/null +++ b/Tree.h @@ -0,0 +1,55 @@ +#ifndef __Tree_H + +#define __Tree_H + +#include "Element.h" + +/* the basic tree structure + * the root of the complete tree is defined where Parent is NULL + */ +typedef struct TreeNodeTag { + struct TreeNodeTag *Left; + struct TreeNodeTag *Right; + Element Item; + char *Name; + short Balance; +} TreeNode; + +/* + * This function will find a particular TreeNode with the given + * Element as the Item. + */ +TreeNode *FindByElement(TreeNode *rootNode, const Element item); + +/* + * This function will return a boolean and determine if the given tree + * (or subtree) is empty. + */ +short IsEmpty(TreeNode *rootNode); + +/* + * This function will insert a given Element into the tree (subtree) and + * perform all neccessary adjustments to the tree. Returns the new root + * node. + */ +TreeNode *Insert(TreeNode *rootNode, Element Item, char *Name); + +/* + * This function will remove all items from the give tree or subtree + */ +void MakeEmpty(TreeNode *rootNode); + +/* + * Deletes a given TreeNode and will perform all neccessary adjustments + * to the tree. Returns the new root node. Note: the Name field will be + * free'd with the TreeNode. + */ +TreeNode *DeleteByElement(TreeNode *rootNode, Element Item); + +/* + * Determines the height of the given tree. Should be used with caution with + * large trees. + */ +long Height(TreeNode *rootNode); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..794494a --- /dev/null +++ b/main.c @@ -0,0 +1,183 @@ +/* A balanced binary search tree(AVLTree) implementation + * Written by Coleman + * Released under GNU GPL Version 2 or later(http://www.gnu.org/) + */ +#include +#include +#include +#include +#include + +#include "Element.h" +#include "Tree.h" + +#define MAXELEMENTSIZE 500000 + +int calcsize(TreeNode *tnode) { + long result = 0; + if(tnode == NULL) + return 0; + result = sizeof(tnode); + result +=(long)sizeof(Element); + result +=(long)sizeof(char) * strlen(tnode->Name); + result += calcsize(tnode->Left); + result += calcsize(tnode->Right); + return result; +} + +void printTree(TreeNode *start, long level) +{ + long i = 0; + if(start == NULL) + return; + printf("%s -> %ld, Balance %d\n", start->Name, + *start->Item, start->Balance); + for(i = 0; i < level; i++) + printf(" "); + printf("Left: "); + printTree(start->Left, level + 1); + printf("\n"); + for(i = 0; i < level; i++) + printf(" "); + printf("Right: "); + printTree(start->Right, level + 1); + if(level == 0) + printf("\n"); +} + +long breadth(TreeNode *start) +{ + if(start == NULL) + return (long)0; + if(start->Left == NULL && start->Right == NULL) + return (long)1; + return (long)(breadth(start->Left) + breadth(start->Right)); +} + +int main(int argc, char** argv) +{ + TreeNode *tree = NULL, *newTree = NULL; + Element newItem = NULL; + char *itemName = NULL; + long i = 0, topsize = 0, tick = 0, last = 0; + long height = 0, oldheight = 0; + + /* get the command argument, used for a custom size parameter */ + if(argc == 2) + topsize = atol(argv[1]); + if(topsize == 0 || topsize > MAXELEMENTSIZE) + topsize = 25; + tick = topsize / 20; + + srand48((long)time(NULL)); + printf("Start --> Finished\n"); + for(i = 0; i < topsize; i++) { + newItem = malloc(sizeof(Element)); + itemName = malloc(sizeof(char) * 11); + sprintf(itemName, "Name%ld", i); + do { + *newItem = lrand48(); + newTree = Insert(tree, newItem, itemName); + if(newTree == NULL) { + if(last == *newItem) { + free(itemName); + free(newItem); + printf("going to try and skip: %ld\n", *newItem); + break; + } else + last = *newItem; + } + } while(newTree == NULL); + if(i % tick == 0) { + printf("."); + fflush(stdout); + } + tree = newTree; + } + printf("\n"); + + /* test out the delete functionality */ + printf("\nGoing to delete the rootNode: %ld\n", *tree->Item); + newTree = DeleteByElement(tree, tree->Item); + if(newTree == NULL) + printf("failure!\n"); + else { + tree = newTree; + printf("success! new root is %ld\n", *tree->Item); + } + printf("\nGoing to delete leftMost child... "); + for(newTree = tree; + newTree != NULL && newTree->Left != NULL; + newTree = newTree->Left) {} + if(newTree != NULL) { + printf("found item: %ld\n", *newTree->Item); + newTree = DeleteByElement(tree, newTree->Item); + if(newTree == NULL) + printf("failure!\n"); + else { + printf("success! new root: %ld\n", *newTree->Item); + tree = newTree; + } + } + /* duplicated from left side */ + printf("\nGoing to delete rightMost child... "); + for(newTree = tree; + newTree != NULL && newTree->Right != NULL; + newTree = newTree->Right) {} + if(newTree != NULL) { + printf("found item: %ld\n", *newTree->Item); + newTree = DeleteByElement(tree, newTree->Item); + if(newTree == NULL) + printf("failure!\n"); + else { + printf("success! new root: %ld\n", *newTree->Item); + tree = newTree; + } + } + printf("\n"); + + if(topsize <= 25) + printTree(tree, 0); + printf("sizeof(TreeNode) %d\n",(int)sizeof(TreeNode)); + printf("sizeof(Element) %d\n",(int)sizeof(Element)); + printf("sizeof(tree) %d\n", calcsize(tree)); + printf("breadth of left tree: %ld right subtree: %ld\n", + breadth(tree->Left), breadth(tree->Right)); + oldheight = Height(tree->Left); + printf("height of left subtree: %ld right subtree: %ld\n", + oldheight, Height(tree->Right)); + + last = (long)(LONG_MAX / 2); + for(i = 0; i < 3; i++) { + printf("\n"); + height = lrand48() % oldheight; + printf("Going to delete a node at depth (max depth %ld): %ld\n", + oldheight, height); + newTree = tree; + do { + if(newTree->Left == NULL) { + if(newTree->Right == NULL) + break; + tick = last + 1; + } else if(newTree->Right == NULL) + tick = last - 1; + else + tick = lrand48(); + newTree = (tick >= last ? newTree->Right : newTree->Left); + height--; + } while(height > 0); + printf("Found a node to delete with Name '%s' and Item %ld\n", + newTree->Name, *newTree->Item); + newTree = DeleteByElement(tree, newTree->Item); + if(newTree == NULL) + printf("failure!\n"); + else { + printf("success! new root: %ld\n", *newTree->Item); + tree = newTree; + } + oldheight = (int)(oldheight * 0.6); + } + + MakeEmpty(tree); + return 0; +}