/*++ Copyright (c) 1995 Microsoft Corporation Module Name: redblack.fnc Abstract: This module implements the red/black trees via macros for structure field independence (so that we can use the same code with different fields by just redefining the macros). This file is included by redblack.c, which also defines the function names. Author: 16-Jun-1995 t-orig Credits: This code is largly based on pseud-code by Cormen, Leiserson and Rivest in "Introduction to Algorithms", MIT press, 1989: QA76.6.C662 Revision History: --*/ /* -- INTRODUCTION: A red/black tree is a binary tree with the following properties: 1) Every node is either red or black 2) Every leaf (NIL, **NOT** NULL) is black. Note that in our implementation this is accomplished by having all leaves be NIL which is black by definition. 3) If a node is red, then both its children are black 4) Every simple path from a node to a descendent leaf contains the same number of black nodes These properties imply that every red/black tree is "approximately balanced", thus red/black trees have logarithmic time operations. The find operation is identical to that of a regular binary tree, and is thus especially fast. Insert and delete operations are complicated by the fact that after the regular binary tree operation, one must take special care to ensure that the red/black properties hold for the new tree. This is accomplished via right and left rotations. Note that the empty tree is denoted by NIL, not NULL. NIL is an actual node passed to all the red/black tree functions, and greatly simplifies coding by eliminating special cases (this also yields faster running time). -- */ PEPNODE LEFT_ROTATE( PEPNODE root, PEPNODE x, PEPNODE NIL ) /*++ Routine Description: Rotates the tree to the left at node x. x y / \ / \ A y ==>> x C / \ / \ B C A B Arguments: root - The root of the Red/Black tree x - The node at which to rotate Return Value: return-value - The new root of the tree (which could be the same as the old root). --*/ { PEPNODE y; y = RIGHT(x); RIGHT(x) = LEFT(y); if (LEFT(y) != NIL){ PARENT(LEFT(y)) = x; } PARENT(y) = PARENT(x); if (PARENT(x) == NIL){ root = y; } else if (x==LEFT(PARENT(x))) { LEFT(PARENT(x)) = y; } else { RIGHT(PARENT(x))= y; } LEFT(y) = x; PARENT(x) = y; return root; } PEPNODE RIGHT_ROTATE( PEPNODE root, PEPNODE x, PEPNODE NIL ) /*++ Routine Description: Rotates the tree to the right at node x. x y / \ / \ y C ==>> A x / \ / \ A B B C Arguments: root - The root of the Red/Black tree x - The node at which to rotate Return Value: return-value - The new root of the tree (which could be the same as the old root). --*/ { PEPNODE y; y = LEFT(x); LEFT(x) = RIGHT(y); if (RIGHT(y) != NIL) { PARENT(RIGHT(y)) = x; } PARENT(y) = PARENT(x); if (PARENT(x) == NIL) { root = y; } else if (x==LEFT(PARENT(x))) { LEFT(PARENT(x)) = y; } else { RIGHT(PARENT(x))= y; } RIGHT(y) = x; PARENT(x) = y; return root; } PEPNODE TREE_INSERT( PEPNODE root, PEPNODE z, PEPNODE NIL ) /*++ Routine Description: Inserts a new node into a tree without preserving the red/black properties. Should ONLY be called by RB_INSERT! This is just a simple binary tree insertion routine. Arguments: root - The root of the red/black tree z - The new node to insert Return Value: return-value - The new root of the tree (which could be the same as the old root). --*/ { PEPNODE x,y; y = NIL; x = root; LEFT(z) = RIGHT(z) = NIL; // Find a place to insert z by doing a simple binary search while (x!=NIL) { y = x; if (KEY(z) < KEY(x)){ x = LEFT(x); } else { x = RIGHT(x); } } // Insert z into the tree PARENT(z)= y; if (y==NIL) { root = z; } else if (KEY(z) END(root)) { root = RIGHT(root); } else { return root; } } return NULL; // Range not found } BOOLEAN CONTAINSRANGE( PEPNODE root, PEPNODE NIL, PVOID StartAddr, PVOID EndAddr ) /*++ Routine Description: Decides if any part of the specified range is represented by a node in the tree. Arguments: root - The root of the red/black tree NIL - NIL pointer for the tree StartAddr - starting address of the range EndAddr - ending address of the range Return Value: TRUE if any byte of the range is inside a node of the tree. FALSE otherwise (no overlap between any entrypoint and the range) --*/ { while (root != NIL) { if (StartAddr <= START(root) && START(root) <= EndAddr) { // START(root) is within the range return TRUE; } if (StartAddr <= END(root) && END(root) <= EndAddr) { // END(root) is within the range return TRUE; } if (StartAddr < START(root)) { root = LEFT(root); } else { root = RIGHT(root); } } return FALSE; // Range is not stored within the tree } PEPNODE FINDNEXT( PEPNODE root, PVOID addr, PEPNODE NIL ) /*++ Routine Description: Finds a node in the red black tree which follows the given address Arguments: root - The root of the red/black tree addr - The address which comes just before the node Return Value: return-value - The node in the tree (entry point of code containing address), or NULL if not found. --*/ { PEPNODE pNode; // If the tree is empty, there is no next node... if (root==NIL){ return NULL; } // Now go down to a leaf while (root != NIL) { if (addr < START(root)) { pNode=root; root = LEFT(root); } else { pNode=root; root = RIGHT(root); } } while (addr > START(pNode)){ if (PARENT(pNode) == NIL){ return NULL; // There is no successor } pNode = PARENT(pNode); } return pNode; } PEPNODE TREE_SUCCESSOR( PEPNODE x, PEPNODE NIL ) /*++ Routine Description: Returns the successor of a node in a binary tree (the successor of x is defined to be the node which just follows x in an inorder traversal of the tree). Arguments: x - The node whose successor is to be returned Return Value: return-value - The successor of x --*/ { PEPNODE y; // If x has a right child, the successor is the leftmost node to the // right of x. if (RIGHT(x) != NIL) { x = RIGHT(x); while (LEFT(x) != NIL) { x = LEFT(x); } return x; } // Else the successor is an ancestor with a left child on the path to x y = PARENT(x); while ((y != NIL) && (x == RIGHT(y))) { x = y; y = PARENT(y); } return y; } PEPNODE RB_DELETE_FIXUP( PEPNODE root, PEPNODE x, PEPNODE NIL ) /*++ Routine Description: Fixes the red/black tree after a delete operation. Should only be called by RB_DELETE Arguments: root - The root of the red/black tree x - Either a child of x, or or a child or x's successor Return Value: return-value - The new root of the red/black tree --*/ { PEPNODE w; // We stop when we either reached the root, or reached a red node (which // means that property 4 is no longer violated). while ((x!=root) && (COLOR(x)==BLACK)) { if (x == LEFT(PARENT(x))) { // x is a left child with sibling w w = RIGHT(PARENT(x)); if (COLOR(w) == RED) { // If w is red it must have black children. We can switch // the colors of w and its parent and perform a left // rotation to bring w to the top. This brings us to one // of the other cases. COLOR(w) = BLACK; COLOR(PARENT(x)) = RED; root = LEFT_ROTATE (root, PARENT(x), NIL); w = RIGHT(PARENT(x)); } if ((COLOR(LEFT(w)) == BLACK) && (COLOR(RIGHT(w)) == BLACK)) { // Here w is black and has two black children. We can thus // change w's color to red and continue. COLOR(w) = RED; x = PARENT(x); } else { if (COLOR(RIGHT(w)) == BLACK) { // Here w is black, its left child is red, and its right child // is black. We switch the colors of w and its left child, // and perform a left rotation at w which brings us to the next // case. COLOR(LEFT(w)) = BLACK; COLOR(w) = RED; root = RIGHT_ROTATE (root, w, NIL); w = RIGHT(PARENT(x)); } // Here w is black and has a red right child. We change w's // color to that of its parent, and make its parent and right // child black. Then a left rotation brings w to the top. // Making x the root ensures that the while loop terminates. COLOR(w) = COLOR(PARENT(x)); COLOR(PARENT(x)) = BLACK; COLOR(RIGHT(w)) = BLACK; root = LEFT_ROTATE (root, PARENT(x), NIL); x = root; } } else { // The symmetric case: x is a right child with sibling w. w = LEFT(PARENT(x)); if (COLOR(w) == RED) { COLOR(w) = BLACK; COLOR(PARENT(x)) = RED; root = RIGHT_ROTATE (root, PARENT(x), NIL); w = LEFT(PARENT(x)); } if ((COLOR(LEFT(w)) == BLACK) && (COLOR(RIGHT(w)) == BLACK)) { COLOR(w) = RED; x = PARENT(x); } else { if (COLOR(LEFT(w)) == BLACK) { COLOR(RIGHT(w)) = BLACK; COLOR(w) = RED; root = LEFT_ROTATE (root, w, NIL); w = LEFT(PARENT(x)); } COLOR(w) = COLOR(PARENT(x)); COLOR(PARENT(x)) = BLACK; COLOR(LEFT(w)) = BLACK; root = RIGHT_ROTATE (root, PARENT(x), NIL); x = root; } } } // end of while loop //printf ("Changing color at %i to BLACK\n", x->intelColor); COLOR(x) = BLACK; return root; } PEPNODE RB_DELETE( PEPNODE root, PEPNODE z, PEPNODE NIL ) /*++ Routine Description: Deletes a node in a red/black tree while preserving the red/black properties. Arguments: root - The root of the red/black tree z - The node to be deleted Return Value: return-value - The new root of the red/black tree --*/ { PEPNODE x,y; COL c; // It's easy to delete a node with at most one child: we only need to // remove it and put the child in its place. It z has at most one child, // we can just remove it. Otherwise we'll replace it with its successor // (which is guaranteed to have at most one child, or else one of its // children would be the succecssor), and delete the successor. if ((LEFT(z) == NIL) || (RIGHT(z) == NIL)) { y = z; } else { y = TREE_SUCCESSOR(z, NIL); } // Recall that y has at most one child. If y has one child, x is set to // it. Else x will be set to NIL which is OK. This way we don't have // to worry about this special case. if (LEFT(y) != NIL){ x = LEFT(y); } else { x = RIGHT(y); } // Now we will remove y from the tree PARENT(x) = PARENT(y); if (PARENT(y) == NIL) { root = x; } else if (y == LEFT(PARENT(y))) { LEFT(PARENT(y)) = x; } else { RIGHT(PARENT(y)) = x; } if (PARENT(x) == z) { PARENT(x) = y; } c = COLOR(y); // Since each node has lots of fields (fields may also change during // the lifetime of this code), I found it safer to copy the // pointers as opposed to data. if (y!=z) { // Now swapping y and z, but remembering color of y PARENT(y) = PARENT(z); if (root == z) { root = y; } else if (z == RIGHT(PARENT(z))) { RIGHT(PARENT(z)) = y; } else { LEFT(PARENT(z)) = y; } LEFT(y) = LEFT(z); if (LEFT(y) != NIL) { PARENT(LEFT(y)) = y; } RIGHT(y) = RIGHT(z); if (RIGHT(y) != NIL) { PARENT(RIGHT(y)) = y; } COLOR(y) = COLOR(z); } // Need to fix the tree (fourth red/black property). if (c == BLACK) { root = RB_DELETE_FIXUP (root, x, NIL); } return root; }