Provide a base class for a red-black binary tree node.
More...
Detailed Description
- Outline
-
-
- Purpose:
- Provide a base class for a red-black binary tree node.
-
- Classes:
-
- See also:
- Component bslalg_rbtreeutil
-
- Description:
- This component provides a single POD-like class,
RbTreeNode
, used to represent a node in a red-black binary search tree. An RbTreeNode
provides the address to its parent, left-child, and right-child nodes, as well as providing a "color" (red or black). RbTreeNode
does not, however, contain "payload" data (e.g., a value), as it is intended to work with generalized tree operations (see bslalg_rbtreenodeutil
). Clients creating a red-black binary search tree must define their own node type that incorporates RbTreeNode
(generally via inheritance), and that maintains the "key" value and any associated data.
-
- Storing Color Information:
- To reduce the memory footprint of the
RbTreeNode
, the color information is stored at the least-significant-bit (LSB) of the parent node. The address of the parent node and the color can be accessed through bit-wise operations. This is possible because all memory addresses are at least 4-bytes aligned, therefore, the 2 LSB of any pointer are always 0.
-
- Usage:
- This section illustrates intended usage of this component.
-
- Example 1: Creating a Function to Print a Red Black Tree:
- This example demonstrates creating a function that prints, to a
FILE
, a tree of RbTreeNode
objects.
- First, we define the signature of a function,
printTree
, that accepts, in addition to an output file and root node, a function pointer argument (supplied by clients) used to print each node's value, note that a node's value is not accessible through RbTreeNode
: void printTree(FILE *output,
const RbTreeNode *rootNode,
void (*printNodeValueCallback)(FILE *, const RbTreeNode *))
{
Now, we define the body of printTree
, which is a recursive function that performs a prefix traversal of the supplied binary tree, printing the value and color of rootNode
before recursively printing its left and then right sub-trees. if (0 == rootNode) {
return;
}
fprintf(output, " [ ");
printNodeValueCallback(output, rootNode);
fprintf(output,
": %s",
rootNode->color() == RbTreeNode::BSLALG_RED ? "RED" : "BLACK");
printTree(output, rootNode->leftChild(), printNodeValueCallback);
printTree(output, rootNode->rightChild(), printNodeValueCallback);
fprintf(output, " ]");
}
Notice that we use FILE
in the context of this usage example to avoid a dependency of standard library streams. Finally, we will use printTree
to print a description of a tree in the next example.
-
- Example 2: Creating a Simple Red-Black Tree:
- This example demonstrates creating a simple tree of integer values using
RbTreeNode
. Note that, in practice, clients should use associated utilities to manage such a tree (see bslalg_rbtreenodeutil
).
- First, we define a node-type,
IntTreeNode
, that inherits from RbTreeNode
: struct IntTreeNode : public RbTreeNode {
int d_value;
};
Then, we define a function printIntNodeValue
to print the value of an integer node. Note that this function's signature matches that required by printTree
(defined in the preceding example): void printIntTreeNodeValue(FILE *output, const RbTreeNode *node)
{
BSLS_ASSERT(0 != node);
fprintf(output, "%d", static_cast<const IntTreeNode*>(node)->d_value);
}
Next, we define main
for our test, and create three nodes that we'll use to construct a tree: int main(int argc, const char *argv[])
{
IntTreeNode A, B, C;
Then, we describe the structure of the tree we wish to construct. A (value: 2, BLACK)
/ \.
/ \.
B (value: 1, RED) C ( value: 3, RED )
Now, we set the properties for the nodes A
, B
, and C
to form a valid tree whose structure matches that description: A.d_value = 2;
A.setColor(RbTreeNode::BSLALG_BLACK);
A.setParent(0);
A.setLeftChild(&B);
A.setRightChild(&C);
B.d_value = 1;
B.setColor(RbTreeNode::BSLALG_RED);
B.setParent(&A);
B.setLeftChild(0);
B.setRightChild(0);
C.d_value = 3;
C.setColor(RbTreeNode::BSLALG_RED);
C.setParent(&A);
C.setLeftChild(0);
C.setRightChild(0);
Finally, we use the printTree
function with the printIntTreeNodeValue
function to print the structure of our tree to stdout
: printTree(stdout, &A, printIntTreeNodeValue);
}
Resulting in a single line of console output: [ 2: BLACK [ 1: RED ] [ 3: RED ] ]
-
- Example 3: Creating a Function To Validate a Red-Black Tree:
- This example demonstrates creating a function to validate the properties of a red-black tree.
- First, we declare the signature of a function
validateRbTree
, which takes two arguments: (1) the address to the root node of a tree, and (2) a comparator function, which is used to compare the payload values of the tree nodes. Note that the parameterized comparator is needed because a node's value is not accessible through the supplied RbTreeNode
. template <class NODE_COMPARATOR>
int validateRbTree(const RbTreeNode *rootNode,
const NODE_COMPARATOR& comparator);
Then, we declare the signature for an auxiliary function, validateRbTreeRaw
, that accepts, additionally, the address of minimum and maximum value nodes, and is needed to recursively apply rule 1: template <class NODE_COMPARATOR>
int validateRbTreeRaw(const RbTreeNode *rootNode,
const RbTreeNode *minNodeValue,
const RbTreeNode *maxNodeValue,
NODE_COMPARATOR comparator);
Now, we define the implementation of validateRbTree
, which simply delegates to validateRbTreeRaw
. template <class NODE_COMPARATOR>
int validateRbTree(const RbTreeNode *rootNode,
NODE_COMPARATOR comparator)
{
return validateRbTreeRaw(rootNode, 0, 0, comparator);
}
Finally, we define the implementation of validateRbTreeRaw
, which tests if rootNode
violates any of the rules defined in the validateRbTree
method documentation, and then recursively calls validateRbTreeRaw
on the left and right sub-trees or rootNode
: template <class NODE_COMPARATOR>
int validateRbTreeRaw(const RbTreeNode *rootNode,
const RbTreeNode *minNodeValue,
const RbTreeNode *maxNodeValue,
NODE_COMPARATOR comparator)
{
enum { INVALID_RBTREE = -1 };
if (0 == rootNode) {
return 0;
}
if ((minNodeValue && comparator(*rootNode, *minNodeValue)) ||
(maxNodeValue && comparator(*maxNodeValue, *rootNode))) {
return INVALID_RBTREE;
}
const RbTreeNode *left = rootNode->leftChild();
const RbTreeNode *right = rootNode->rightChild();
if ((left && left->parent() != rootNode) ||
(right && right->parent() != rootNode)) {
return INVALID_RBTREE;
}
if (RbTreeNode::BSLALG_RED == rootNode->color()) {
if ((left && left->color() != RbTreeNode::BSLALG_BLACK) ||
(right && right->color() != RbTreeNode::BSLALG_BLACK)) {
return INVALID_RBTREE;
}
}
int leftDepth = validateRbTreeRaw(rootNode->leftChild(),
minNodeValue,
rootNode,
comparator);
int rightDepth = validateRbTreeRaw(rootNode->rightChild(),
rootNode,
maxNodeValue,
comparator);
if (leftDepth < 0 || rightDepth < 0) {
return INVALID_RBTREE;
}
if (leftDepth != rightDepth) {
return INVALID_RBTREE;
}
return (rootNode->color() == RbTreeNode::BSLALG_BLACK)
? leftDepth + 1
: leftDepth;
}