// Header file #include "RedBlackTreeUtility.h" // constants const char SEMICOLON_BREAK[] = "; "; const char THICK_DIVIDER = '='; const char THIN_DIVIDER = '-'; /* Data Structures typedef struct RedBlackNodeStruct { Colors color; (typedef enum { BLACK = 66, RED = 82 } Colors;) char data; struct RedBlackNodeStruct *parentPtr, *leftChildPtr, *rightChildPtr; } RedBlackNodeType; typedef enum { PREORDER_TRAVERSE = 101, INORDER_TRAVERSE = 102, POSTORDER_TRAVERSE = 103, NODE_COLOR = 191, NODE_DATA = 192 } ControlCodes; Data Structures */ // functions /* Name: clearTree Process: recursively returns dynamically allocated data to the OS using a post order traversal strategy Function input/parameters: working pointer for recursion (RedBlackNodeType *) Function output/parameters: none Function output/returned: empty tree (NULL) Device input/---: none Device output/---: none Dependencies: free */ RedBlackNodeType *clearTree(RedBlackNodeType *localPtr) { // recurse through the left children if (localPtr->leftChildPtr != NULL) { clearTree(localPtr->leftChildPtr); } // recurse through the right children if (localPtr->rightChildPtr != NULL) { clearTree(localPtr->rightChildPtr); } // otherwise, free the local pointer if at end of getting to bottom tree free(localPtr); // return NULL since no tree left return NULL; } /* Name: copyTree Process: copies a Red Black tree from another Red Black tree using a pre order traversal strategy, appropriately links child parent pointers to current node Function input/parameters: pointer to source node (RedBlackNodeType *) Function output/parameters: none Function output/returned: pointer to destination node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: createNodeFromNode, copyTree (recursively) */ RedBlackNodeType *copyTree(RedBlackNodeType *srcPtr) { // make a storage pointer RedBlackNodeType *copyPtr = NULL; // check for not equal to null if (srcPtr != NULL) { // create the pointer given data copyPtr = createNodeFromNode(srcPtr); // recurse left child copyPtr->leftChildPtr = copyTree(srcPtr->leftChildPtr); // recurse right child copyPtr->rightChildPtr = copyTree(srcPtr->rightChildPtr); } // return copyPtr return copyPtr; } /* Name: createNodeFromData Process: creates a Red Black tree node from the node data given, all links are set to NULL Function input/parameters: data (char), color (int) Function output/parameters: none Function output/returned: pointer to created node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: malloc */ RedBlackNodeType *createNodeFromData(char data, int nodeColor) { // define a new node RedBlackNodeType *newNode; // allocate memory for the newNode (RedBlackNodeType) newNode = malloc(sizeof(RedBlackNodeType)); // define the color as the new node's color newNode->color = nodeColor; // define the data of the new node to be the input data newNode->data = data; // set the node's pointers according to spec newNode->parentPtr = NULL; newNode->leftChildPtr = NULL; newNode->rightChildPtr = NULL; // return the created node return newNode; } /* Name: createNodeFromNode Process: allocates memory for node, copies an individual tree node from another node, all links are set to NULL Function input/parameters: pointer to source node (RedBlackNodeType *) Function output/parameters: none Function output/returned: pointer to destination node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: one line of code - createNodeFromData */ RedBlackNodeType *createNodeFromNode(RedBlackNodeType *srcPtr) { // return the created node from the given data return createNodeFromData(srcPtr->data, srcPtr->color); } /* Name: displayInOrder Process: traverses across tree, prints semicolon-delimited list, displays "data / color" using in order strategy Function input/parameters: pointer to current working node (RedBlackNodeType *), pointer to row start flag for first line of output (bool *) Function output/parameters: none Function output/returned: none Device input/---: none Device output/---: displayed as specified Dependencies: printf, displayInOrder (recursively) */ void displayInOrder(RedBlackNodeType *wkgPtr, bool *rowStartFlag) { // check to see if the working pointer is null if (wkgPtr != NULL) { // check to see if the rowStartFlag is true if(rowStartFlag) { // if true, print the order printf("Inorder : "); // set the rowStartFlag to false rowStartFlag = false; } // recurse to left node in tree displayInOrder(wkgPtr->leftChildPtr, rowStartFlag); // print out the string printf(" %c/%c%s", wkgPtr->data, wkgPtr->color, SEMICOLON_BREAK); // recurse to right node in tree displayInOrder(wkgPtr->rightChildPtr, rowStartFlag); } // no return, void. } /* Name: displayPostOrder Process: traverses across tree, prints semicolon-delimited list, displays "data / color" using post order strategy Function input/parameters: pointer to current working node (RedBlackNodeType *), pointer to row start flag for first line of output (bool *) Function output/parameters: none Function output/returned: none Device input/---: none Device output/---: displayed as specified Dependencies: printf, displayPostOrder (recursively) */ void displayPostOrder(RedBlackNodeType *wkgPtr, bool *rowStartFlag) { // check to see if the working pointer is null if (wkgPtr != NULL) { // check to see if the rowStartFlag is true if (rowStartFlag) { // if true, print the order printf("Postorder: "); // set the rowStartFlag to false rowStartFlag = false; } // recurse to left node in tree displayPostOrder(wkgPtr->leftChildPtr, rowStartFlag); // recurse to right node in tree displayPostOrder(wkgPtr->rightChildPtr, rowStartFlag); // print out the string printf(" %c/%c%s", wkgPtr->data, wkgPtr->color, SEMICOLON_BREAK); } // no return, void. } /* Name: displayPreOrder Process: traverses across tree, prints semicolon-delimited list, displays "data / color" using pre order strategy Function input/parameters: pointer to current working node (RedBlackNodeType *), pointer to row start flag for first line of output (bool *) Function output/parameters: none Function output/returned: none Device input/---: none Device output/---: displayed as specified Dependencies: printf, displayPreOrder (recursively) */ void displayPreOrder(RedBlackNodeType *wkgPtr, bool *rowStartFlag) { // check to see if the working pointer is null if (wkgPtr != NULL) { // check to see if the rowStartFlag is true if (rowStartFlag) { // if true, print the order printf("Preorder : "); // set the rowStartFlag to false rowStartFlag = false; } // print out the string printf(" %c/%c%s", wkgPtr->data, wkgPtr->color, SEMICOLON_BREAK); // recurse to left node in tree displayPreOrder(wkgPtr->leftChildPtr, rowStartFlag); // recurse to right node in tree displayPreOrder(wkgPtr->rightChildPtr, rowStartFlag); } // no return, void. } /* Name: displayTree Process: controller function for calling appropriate tree traversal Function input/parameters: pointer to current tree root (RedBlackNodeType *), control codes INORDER_TRAVERSE, POSTORDER_TRAVERSE, PREORDER_TRAVERSE Function output/parameters: none Function output/returned: none Device input/---: none Device output/---: displayed as specified Dependencies: displayInOrder, displayPostOrder, displayPreOrder, printf */ void displayTree(RedBlackNodeType *rootPtr, ControlCodes traverseCode) { bool rowStarter = true; // check to see which ControlCode was used, display based on this. if(traverseCode == PREORDER_TRAVERSE) { printf("\n"); // use the preorder traversal function displayPreOrder(rootPtr, &rowStarter); printf("\n"); } else if(traverseCode == INORDER_TRAVERSE) { printf("\n"); // use the in order traversal function displayInOrder(rootPtr, &rowStarter); printf("\n"); } else { printf("\n"); // use the postorder traversal function displayPostOrder(rootPtr, &rowStarter); printf("\n"); } // no return, void. } /* Name: findTreeHeight Process: recursively finds height of tree Function input/parameters: working pointer for recursion (RedBlackNodeType *) Function output/parameters: none Function output/returned: height of tree (int) Device input/---: none Device output/---: none Dependencies: getMax */ int findTreeHeight(RedBlackNodeType *localPtr) { // check to see if at null if(localPtr == NULL) { // return negative one since either empty or at bottom of tree return -1; } else { // traverse the left and right subtrees int leftTreeSize = findTreeHeight(localPtr->leftChildPtr); int rightTreeSize = findTreeHeight(localPtr->rightChildPtr); // return the max of the subtrees, add one for the local node return getMax(leftTreeSize, rightTreeSize) + 1; } } /* Name: initializeRBT Process: returns NULL to tree root pointer to prepare tree for use Function input/parameters: none Function output/parameters: none Function output/returned: NULL Device input/---: none Device output/---: none Dependencies: none */ RedBlackNodeType *initializeRBT() { // return NULL to begin tree return NULL; } /* Name: insert Process: sets first node if tree is empty, otherwise calls helper to set nodes, then calls RBT resolver to rebalance tree if node is successfully inserted by helper, prints inserting/resolving statement and root color set to black Function input/parameters: tree root pointer (RedBlackNodeType *), data to be inserted (char) Function output/parameters: none Function output/returned: pointer to updated tree root (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: printf, createNodeFromData, insertHelper, resolveRBT */ RedBlackNodeType *insert(RedBlackNodeType *treeRoot, char inData) { RedBlackNodeType *insertedNode; // check if not null if(treeRoot != NULL) { // use insertHelper to get to where the data needs to go insertedNode = insertHelper(treeRoot, inData); // print out what data is being added, tell user being resolved printf("\nInserting %c and resolving\n", inData); // use resolveRBT to get the tree balanced correctly resolveRBT(&treeRoot, insertedNode); } else { // if inserting the root node // set the root with createNodeFromData, node is black. treeRoot = createNodeFromData(inData, BLACK); // tell the user inserting the data printf("\nInserting %c and resolving\n", inData); // tell the user that the root was set, and is black. printf(" - Root color set to black\n"); /* AN ALTERNATIVE COULD BE TO INSTEAD USE THE RESOLVE FUNCTION INSTEAD OF DOING IT BY HAND*/ } // return treeRoot return treeRoot; } /* Name: insertHelper Process: recursively finds place to add node, places node using "look down" strategy, returns newly inserted node pointer, if duplicated nodes/keys attempted, returns NULL Function input/parameters: working pointer for recursion (RedBlackNodeType *), data to be inserted (char) Function output/parameters: none Function output/returned: pointer to inserted node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: createNodeFromData, insertHelper (recursively) */ RedBlackNodeType *insertHelper(RedBlackNodeType *wkgPtr, char inData) { // compare between current node and inData int diffChar = inData - wkgPtr->data; // check for moving left (diffChar < 0) if(diffChar < 0) { // check for left child NULL if(wkgPtr->leftChildPtr == NULL) { // assign current left child to a new node wkgPtr->leftChildPtr = createNodeFromData(inData, RED); // return pointer to the node added return wkgPtr->leftChildPtr; } // return recursion return insertHelper(wkgPtr->leftChildPtr, inData); } // otherwise, check for moving right (diffChar > 0) else if(diffChar > 0) { // check for right child NULL if(wkgPtr->rightChildPtr == NULL) { // assign current right child to a new node wkgPtr->rightChildPtr = createNodeFromData(inData, RED); // return pointer to the node added return wkgPtr->rightChildPtr; } // return recursion return insertHelper(wkgPtr->rightChildPtr, inData); } // otherwise, there is a duplicate else { // return NULL return NULL; } } /* Name: isEmpty Process: returns true if tree is empty, false otherwise Function input/parameters: tree root pointer (RedBlackNodeType *) Function output/parameters: none Function output/returned: result of test (bool) Device input/---: none Device output/---: none Dependencies: none */ bool isEmpty(RedBlackNodeType *localPtr) { // return test of root pointer is NULL return localPtr == NULL; } /* Name: resolveRBT Process: recursively checks tree for need to rebalance, conducts rebalance as needed Function input/parameters: address of tree root pointer (RedBlackNodeType **), working pointer for recursion which starts at the location of the inserted node (RedBlackNodeType *) Function output/parameters: none Function output/returned: none Device input/---: none Device output/---: none Dependencies: printf, rotateLeft, rotateRight, swapColors, displayTreeStructure, displayDivider */ void resolveRBT(RedBlackNodeType **treeRoot, RedBlackNodeType *wkgPtr) { // declare parPtr, grndParPtr, unclePtr, grtGrndPtr RedBlackNodeType *wkgParentPtr; RedBlackNodeType *wkgGrandParentPtr; RedBlackNodeType *wkgUnclePtr; RedBlackNodeType *wkgGreatGrandParentPtr; // check for parent and grandparent exists if(wkgPtr->parentPtr != NULL && wkgPtr->parentPtr->parentPtr != NULL) { // set all my relatives, will need to know which child I am wkgParentPtr = wkgPtr->parentPtr; wkgGrandParentPtr = wkgParentPtr->parentPtr; if (wkgParentPtr->leftChildPtr->data == wkgPtr->data) { // set the uncle pointer to the parent's right child wkgUnclePtr = wkgParentPtr->rightChildPtr; } else { // set the uncle pointer to the parent's left child wkgUnclePtr = wkgParentPtr->leftChildPtr; } wkgGreatGrandParentPtr = wkgGrandParentPtr->parentPtr; // check for red uncle if(wkgUnclePtr != NULL && wkgUnclePtr->color == RED) { // change parent and uncle to black wkgParentPtr->color = BLACK; wkgUnclePtr->color = BLACK; // change grandparent to red wkgGrandParentPtr->color = RED; // recurse with the grandparent resolveRBT(treeRoot, wkgGrandParentPtr); } // otherwise, assume uncle is black else { // check for parent is left of grandparent if(wkgGrandParentPtr->leftChildPtr->data == wkgParentPtr->data) { // check for child is left of parent (Left Left case) if(wkgParentPtr->leftChildPtr->data == wkgPtr->data) { printf("\nLL Case, rotate right\n"); // use rotate right with grandparent(old) and parent(new) // link the parent to grandparent's parent // swap colors called with parent and grandparent } // check for child is right of parent (Left Right case) if(wkgParentPtr->rightChildPtr->data == wkgPtr->data) { printf("\nLR Case, rotate right, recurse\n"); // use rotate right with parent(old) and createdNode(new) // call resolveRBT with parent as createdNode (will treat as LLC) } } // check for parent is right of grandparent if(wkgGrandParentPtr->rightChildPtr->data == wkgParentPtr->data) { // check for child is right of parent (Right Right case) if(wkgParentPtr->rightChildPtr->data == wkgPtr->data) { printf("\nRR Case, rotate left\n"); // use rotate left with grandparent(old) and parent(new) // link the parent to grandparent's parent // swap colors called with parent and grandparent } // check for child is left of parent (Right Left case) if(wkgParentPtr->leftChildPtr->data == wkgPtr->data) { printf("\nRL Case, rotate left, recurse\n"); // use rotate left with parent(old) and createdNode(new) // call resolveRBT with parent as createdNode (will treat as RRC) } } } // end of par/gpr test } // check for current node is root pointer if(wkgPtr == *treeRoot) { // paint it black wkgPtr->color = BLACK; } // no return, void. } /* Name: rotateLeft Process: "rotates" nodes to the left by resetting pointers to related nodes Function input/parameters: old and new top node pointers (RedBlackNodeType *) Function output/parameters: none Function output/returned: pointer to new top node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: none */ RedBlackNodeType *rotateLeft(RedBlackNodeType *newTopPtr, RedBlackNodeType *oldTopPtr) { // set the old top's parent's right child to the new top oldTopPtr->parentPtr->rightChildPtr = newTopPtr; // set the old top's right child to the new top's left child oldTopPtr->rightChildPtr = newTopPtr->leftChildPtr; // set the new top's parent to old top's parent newTopPtr->parentPtr = oldTopPtr->parentPtr; // set the new top's left child to old top newTopPtr->leftChildPtr = oldTopPtr; // set the old top's parent to new top oldTopPtr->parentPtr = newTopPtr; // return new top return newTopPtr; } /* Name: rotateRight Process: "rotates" nodes to the right by resetting pointers to related nodes Function input/parameters: old and new top node pointers (RedBlackNodeType *) Function output/parameters: none Function output/returned: pointer to new top node (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: none */ RedBlackNodeType *rotateRight(RedBlackNodeType *newTopPtr, RedBlackNodeType *oldTopPtr) { // set the old top's parent's left child to the new top oldTopPtr->parentPtr->leftChildPtr = newTopPtr; // set the old top's left child to the new top's right child oldTopPtr->leftChildPtr = newTopPtr->rightChildPtr; // set the new top's parent to old top's parent newTopPtr->parentPtr = oldTopPtr->parentPtr; // set the new top's right child to old top newTopPtr->rightChildPtr = oldTopPtr; // set the old top's parent to new top oldTopPtr->parentPtr = newTopPtr; // return new top return newTopPtr; } /* Name: swapColors Process: swaps or exchanges colors between two nodes Function input/parameters: pointers to two nodes (RedBlackNodeType *) Function output/parameters: none Function output/returned: updated pointers to two nodes (RedBlackNodeType *) Device input/---: none Device output/---: none Dependencies: none */ void swapColors(RedBlackNodeType *one, RedBlackNodeType *other) { // define temporary holding place for one color int tempColor = one->color; // swap the colors using the temp. one->color = other->color; other->color = tempColor; // no return, void. } //////////////////////////////////////////////////////////////////////////////// // No student coding below this point //////////////////////////////////////////////////////////////////////////////// /* Name: displayAtTreeLevel Process: displays one horizontal "level" of a tree using text graphics with appropriate spacing and appropriate number of nodes Function input/parameters: recursive working node (RedBlackNodeType *), node height, display level, working level (int), row start flag (bool), control code for either data or color Function output/parameters: none Function output/returned: updated row start flag (bool *) Device input/file: none Device output/monitor: none Dependencies: displayValue, displayEmptyNodeSpaces */ void displayAtTreeLevel( RedBlackNodeType *workingNode, int nodeHeight, int displayLevel, int workingLevel, bool *rowStartFlag, ControlCodes ctrlCode ) { char charOut = workingNode->data; if( ctrlCode == NODE_COLOR ) { charOut = (char)workingNode->color; } if( workingLevel == displayLevel ) { displayValue( charOut, nodeHeight, workingLevel, rowStartFlag ); } else { if( workingNode->leftChildPtr != NULL ) { displayAtTreeLevel( workingNode->leftChildPtr, nodeHeight, displayLevel, workingLevel + 1, rowStartFlag, ctrlCode ); } else { displayEmptyNodeSpaces( nodeHeight, displayLevel, workingLevel + 1, rowStartFlag, ctrlCode ); } if( workingNode->rightChildPtr != NULL ) { displayAtTreeLevel( workingNode->rightChildPtr, nodeHeight, displayLevel, workingLevel + 1, rowStartFlag, ctrlCode ); } else { displayEmptyNodeSpaces( nodeHeight, displayLevel, workingLevel + 1, rowStartFlag, ctrlCode ); } } } /* Name: displayChars Process: recursively displays a specified number of specified characters Function input/parameters: number of characters (int), character to be displayed (char) Function output/parameters: none Function output/returned: none Device input/file: none Device output/monitor: characters displayed as specified Dependencies: printf */ void displayChars( int numChars, char outChar ) { if( numChars > 0 ) { printf( "%c", outChar ); displayChars( numChars - 1, outChar ); } } /* Name: displayDivider Process: displays divider of correct length for tree using either THICK_DIVIDER or THIN_DIVIDER depending on the control code, adds one endline to thick divider, two to thin for spacing Function input/parameters: number of characters (int), character to be displayed (char) Function output/parameters: none Function output/returned: none Device input/file: none Device output/monitor: characters displayed as specified Dependencies: printf */ void displayDivider( RedBlackNodeType *rootPtr, char dividerChar ) { int treeHeight = findTreeHeight( rootPtr ); int numChars = toPower( 2, treeHeight + 2 ); displayChars( numChars, dividerChar ); if( dividerChar == THIN_DIVIDER ) { printf( "\n" ); } printf( "\n" ); } /* Name: displayEmptyNodeSpaces Process: displays the appropriate number of dashes for a given level for null nodes, can display either dashes or 'B's Function input/parameters: node height, display level, working level (int) pointer to row start flag (bool *), control code for either data or color Function output/parameters: none Function output/returned: updated row start flag (bool *) Device input/file: none Device output/monitor: characters displayed as specified Dependencies: toPower, displayValue */ void displayEmptyNodeSpaces( int nodeHeight, int displayLevel, int workingLevel, bool *rowStartFlag, ControlCodes ctrlCode ) { int nodesToDisplay = toPower( 2, displayLevel - workingLevel ); char charOut = SPACE; if( displayLevel == workingLevel ) { charOut = DASH; if( ctrlCode == NODE_COLOR ) { charOut = 'B'; } } while( nodesToDisplay > 0 ) { displayValue( charOut, nodeHeight, displayLevel, rowStartFlag ); nodesToDisplay--; } } /* Name: displayTreeStructure Process: repeatedly calls other function to display the structure of an RB tree, can display either dashes or 'B's, displays error message if empty tree Function input/parameters: tree root pointer (RedBlackNodeType *), control code for either data or color Function output/parameters: none Function output/returned: none Device input/file: none Device output/monitor: characters displayed as specified Dependencies: findTreeHeight, displayAtTreeLevel, printf */ void displayTreeStructure( RedBlackNodeType *rbTreeRoot, ControlCodes ctrlCode ) { int displayLevel, nodeHeight = findTreeHeight( rbTreeRoot ) + 2; int workingLevel = 1; bool rowStartFlag; if( rbTreeRoot != NULL ) { for( displayLevel = 1; displayLevel <= nodeHeight; displayLevel++ ) { rowStartFlag = true; displayAtTreeLevel( rbTreeRoot, nodeHeight, displayLevel, workingLevel, &rowStartFlag, ctrlCode ); printf( "\n" ); } } else { printf( "\nEmpty Tree - No Display" ); } } /* Name: displayValue Process: displays a tree character value or color letter (R/B) after a calculated set of leading spaces Function input/parameters: character data (char), node height, working level (int), pointer to row start flag (bool *) Function output/parameters: updated row start flag (bool *) Function output/returned: none Device input/file: none Device output/monitor: character displayed as specified Dependencies: toPower, displayChars, printf */ void displayValue( char data, int nodeHeight, int workingLevel, bool *rowStartFlag ) { int leadingSpaces; if( *rowStartFlag ) { leadingSpaces = toPower( 2, nodeHeight - workingLevel ); *rowStartFlag = false; } else { leadingSpaces = toPower( 2, nodeHeight - workingLevel + 1 ) - 1; } displayChars( leadingSpaces, SPACE ); printf( "%c", data ); } /* Name: getMax Process: finds the maximum between two values, returns Function input/parameters: two values (int) Function output/parameters: none Function output/returned: maximum of two values (int) Device input/file: none Device output/monitor: none Dependencies: none */ int getMax( int one, int other ) { int max = one; if( other > max ) { max = other; } return max; } /* Name: toPower Process: recursively calculates a base to exponent value for positive integer exponents Function input/parameters: base, exponent (int) Function output/parameters: none Function output/returned: calculated value (int) Device input/file: none Device output/monitor: none Dependencies: none */ int toPower( int base, int exponent ) { if( exponent > 0 ) { return toPower( base, exponent - 1 ) * base; } return 1; }