/* * File: bst.c * ----------- * This file implements the bst.h interface, which provides a * general implementation of binary search trees. */ #include #include "genlib.h" #include "bst.h" /* * Types: treeT, keyT, clientDataT, clientNodeT * -------------------------------------------- * These types are all void*, but the typedefs make the code easier to read * and serve to remind the implementor of the conceptual types involved. * * Because the implementation does not know the structure of a * node, pointers to nodes cannot be defined explicitly and must * be represented using void*. For readability, the code declares * any void* pointers that are in fact trees to be of type treeT. * This type will be used whether thinking of the tree holistically or * as a pointer to an individual node. * * The nodes in a tree have the following structure: * * ___________________ * | | * | | * | client data | * | | * | (contains key, | * | only size known | * | to implementation)| * | | * =================== * | | * | left | * ___________________ * | | * | right | * ___________________ * * * * KeyT and clientNodeT are void* to allow the client control over * the types used in the BST. Note that a clientNodeT will point to * the same location as a treeT, the difference is whether the intent is * to return the value to the client (who will cast it to look only * at the "client part" of the node), or used by the implemention (which * knows the entire structure, but cannot manipulate the "client part"). * Sometimes this distinction is not easily made, so one must be aware * that the two types are essentially the same. * * clientDataT will refer to any data the client may pass to call-back * functions. */ typedef void *treeT, *keyT, *clientNodeT, *clientDataT; /* * Type: bstCDT * ------------ * This type is the concrete type used to represent the bstADT. */ typedef struct bstCDT { treeT root; int userSize, totalSize; cmpFnT cmpFn; nodeInitFnT nodeInitFn; } bstCDT; /* * Type: bstLinksT * -------------- * This record is allocated at the end of the client's structure * and is used to maintain the structure of the tree. The code * calls BSTLinks() on the node pointer to derive this address. */ typedef struct { treeT left, right; } bstLinksT; /* Private function prototypes */ static treeT *RecFindNode(bstADT bst, treeT t, keyT kp); static treeT RecInsertNode(bstADT bst, treeT *tptr, keyT kp, clientDataT clientData); static treeT RecDeleteNode(bstADT bst, treeT *tptr, keyT kp); static treeT DeleteTargetNode(bstADT bst, treeT *tptr); static void RecMapBST(nodeFnT fn, bstADT bst, treeT t, traversalOrderT order, clientDataT clientData); static bstLinksT *BSTLinks(bstADT bst, treeT t); /* Exported entries */ bstADT NewBST(int size, cmpFnT cmpFn, nodeInitFnT nodeInitFn) { bstADT bst; bst = (bstADT) GetBlock(sizeof (bstCDT)); bst->root = NULL; bst->userSize = size; bst->totalSize = bst->userSize + sizeof(bstLinksT); bst->cmpFn = cmpFn; bst->nodeInitFn = nodeInitFn; return (bst); } void FreeBST(bstADT bst, nodeFnT freeNodeFn) { MapBST(freeNodeFn, bst, PostOrder, NULL); FreeBlock(bst); } /* * Implementation notes: FindBSTNode, RecFindNode * ---------------------------------------------- * The FindBSTNode function simply calls RecFindNode to do * the work. The recursive function takes the address of * the current node along with the original arguments. */ clientNodeT FindBSTNode(bstADT bst, keyT kp) { return (RecFindNode(bst, bst->root, kp)); } static treeT *RecFindNode(bstADT bst, treeT t, keyT kp) { bstLinksT *linksPtr; int sign; if (t == NULL) return (NULL); sign = bst->cmpFn(kp, t); if (sign == 0) return (t); linksPtr = BSTLinks(bst, t); if (sign < 0) { return (RecFindNode(bst, linksPtr->left, kp)); } else { return (RecFindNode(bst, linksPtr->right, kp)); } } /* * Implementation notes: InsertBSTNode, RecInsertNode * -------------------------------------------------- * The InsertBSTNode function is implemented as a simple wrapper * to RecInsertNode, which does all the work. The difference * between the prototypes is that RecInsertNode takes a pointer * to the root of the current subtree as an extra argument. */ clientNodeT InsertBSTNode(bstADT bst, keyT kp, clientDataT clientData) { return (RecInsertNode(bst, &bst->root, kp, clientData)); } static treeT RecInsertNode(bstADT bst, treeT *tptr, keyT kp, clientDataT clientData) { bstLinksT *linksPtr; treeT t; int sign; t = *tptr; if (t == NULL) { t = GetBlock(bst->totalSize); bst->nodeInitFn(t, kp, clientData); linksPtr = BSTLinks(bst, t); linksPtr->left = linksPtr->right = NULL; *tptr = t; return (t); } sign = bst->cmpFn(kp, t); if (sign == 0) return (t); linksPtr = BSTLinks(bst, t); if (sign < 0) { return (RecInsertNode(bst, &linksPtr->left, kp, clientData)); } else { return (RecInsertNode(bst, &linksPtr->right, kp, clientData)); } } /* * Implementation notes: DeleteBSTNode, RecDeleteNode * -------------------------------------------------- * The first step in deleting a node is to find it using binary * search, which is performed by these two functions. If the * node is found, DeleteTargetNode does the actual deletion. */ clientNodeT DeleteBSTNode(bstADT bst, keyT kp) { return (RecDeleteNode(bst, &bst->root, kp)); } static treeT RecDeleteNode(bstADT bst, treeT *tptr, keyT kp) { bstLinksT *linksPtr; treeT t; int sign; t = *tptr; if (t == NULL) return (NULL); sign = bst->cmpFn(kp, t); if (sign == 0) { return (DeleteTargetNode(bst, tptr)); } linksPtr = BSTLinks(bst, t); if (sign < 0) { return (RecDeleteNode(bst, &linksPtr->left, kp)); } else { return (RecDeleteNode(bst, &linksPtr->right, kp)); } } /* * Implementation notes: DeleteTargetNode * -------------------------------------- * This function deletes the node whose address is passed by * reference in tptr. The easy case occurs when either of the * children is NULL; all you need to do is replace the node with * its non-NULL child. If both children are non-NULL, this code * finds the rightmost descendent of the left child; this node * may not be a leaf, but will have no right child. Its left * child replaces it in the tree, after which the replacement * node is moved to the position occupied by the target node. */ static treeT DeleteTargetNode(bstADT bst, treeT *tptr) { treeT target, *rptr; bstLinksT *tlinksPtr, *rlinksPtr; target = *tptr; tlinksPtr = BSTLinks(bst, target); if (tlinksPtr->left == NULL) { *tptr = tlinksPtr->right; } else if (tlinksPtr->right == NULL) { *tptr = tlinksPtr->left; } else { rptr = &tlinksPtr->left; rlinksPtr = BSTLinks(bst, *rptr); while (rlinksPtr->right != NULL) { rptr = &rlinksPtr->right; rlinksPtr = BSTLinks(bst, *rptr); } *tptr = *rptr; *rptr = rlinksPtr->left; rlinksPtr->left = tlinksPtr->left; rlinksPtr->right = tlinksPtr->right; } return (target); } /* * Implementation notes: MapBST, RecMapBST * --------------------------------------- * The MapBST function is implemented as a wrapper to the * recursive function RecMapBST, which does the actual work. */ void MapBST(nodeFnT fn, bstADT bst, traversalOrderT order, clientDataT clientData) { RecMapBST(fn, bst, bst->root, order, clientData); } static void RecMapBST(nodeFnT fn, bstADT bst, treeT t, traversalOrderT order, clientDataT clientData) { bstLinksT *linksPtr; if (t != NULL) { linksPtr = BSTLinks(bst, t); if (order == PreOrder) fn(t, clientData); RecMapBST(fn, bst, linksPtr->left, order, clientData); if (order == InOrder) fn(t, clientData); RecMapBST(fn, bst, linksPtr->right, order, clientData); if (order == PostOrder) fn(t, clientData); } } /* Low-level functions */ clientNodeT BSTRoot(bstADT bst) { return (bst->root); } clientNodeT BSTLeftChild(bstADT bst, treeT np) { bstLinksT *linksPtr; if (np == NULL) Error("BSTLeftChild: Argument is NULL"); linksPtr = BSTLinks(bst, np); return (linksPtr->left); } clientNodeT BSTRightChild(bstADT bst, treeT np) { bstLinksT *linksPtr; if (np == NULL) Error("BSTRightChild: Argument is NULL"); linksPtr = BSTLinks(bst, np); return (linksPtr->right); } /* * Function: BSTLinks * Usage: linksPtr = BSTLinks(bst, t); * ---------------------------- * This function determines the address of the data record * containing the pointers for the binary tree. This record * appears at the end of the user structure that begins at t. */ static bstLinksT *BSTLinks(bstADT bst, treeT t) { return ((bstLinksT *) ((char *) t + bst->userSize)); }