HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

My Red-Black Tree Implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationredtreeblack

Problem

I worked on a Red-Black tree implementation in C++, and it works as it should. However, I want to see what else I can do to make this code faster/more clean, etc.

Any particular suggestions?

Edit: code I want most looked at is below (insertion into the RBtree)

```
/
RBInsert
Purpose: Insert a new datum into tree
*/
template
void RBTree::RBinsert(T data) {
RBNode* x;
RBNode* y;
RBNode* temp = RBTree::search(data);

if(temp != RBTree::nil) {
cout * z = new RBNode();
z->data = data;
y = this->RBTree::nil;
x = this->root;
while(x != RBTree::nil) {
y = x;
if (z->data data)
x = x->left;
else
x = x->right;
}

z->parent = y;
if(y == RBTree::RBTree::nil)
root = z;
else if(z->data data)
y->left = z;
else
y->right = z;
z->left = RBTree::nil;
z->right = RBTree::nil;
z->changeColor(RBNode::red);
RBTree::RBinsertFixup(z);
}

/
RBinsertFixup
Purpose: After inserting, calls this
to fix red-black properties of tree
*/
template
void RBTree::RBinsertFixup(RBNode* z) {
RBNode* y;
while(z->parent->color == RBNode::red && z->parent != RBTree::nil) {
if(z->parent == z->parent->parent->left) {
y = z->parent->parent->right;

// Case 1
if(y->color == RBNode::red) {
z->parent->changeColor(RBNode::black);
y->changeColor(RBNode::black);
z->parent->parent->changeColor(RBNode::red);
z = z->parent->parent;
} else {

// Case 2
if(z == z->parent->right) {
z = z->parent;
leftRotate(z);
}

// Case 3
z->parent->changeColor(RBNode::black);
z->parent->parent->changeColor(RBNode::red);
rightRotate(z->parent->parent);

Solution

Its hard to review because you did not provide enough to compile.

DON'T use using in a header file that is just the global namespace.

using std::ifstream;


This would have been fine if you had placed your stuff in your own namespace. But because it is in global namespace your choices affect other people. ie. Your code could potentially break my code.

The prefix RB is unnecessary and very C like. You should really use your own namespace RB as this resolves the same issue that you are trying to avoid. I notice from the above comments that you got this idea from a book. I would advice this book is way out of date as modern C++ books would not use this style.

Interface:

void RBwrite(RBNode*);


I don't understand what this is for.

Are you asking this tree to write out another tree? If you are asking the class to serialize the tree then I migth see it but then I would make the function static. But then I have to ask write it to where? You should be providing a location to stream the data.

Personally I would replace the above call with:

friend std::ostream& operator const& data)
{
    // write tree to stream
    // Internally you can call RBWrite() but passing a stream
    // I would make RBWrite private
}


Then the converse:

void RBread();

Again where are you reading from? Again I would change the interface:

friend std::istream& operator>>(std::istream& stream, RBTree& data)
{
    // read tree from stream
    // Internally you can call RBRead() but passing a stream
    // I would make RBRead private
}


With the changes in the interface you are more C++ like.

Potential Syntax Errors;

bool treeCreated = false;


This does not compile in C++98 (though it is valid C++11). But worse it suggests that a tree can be in an uncreated state. Once the constructor is finished the object should be fully formed and ready for use. If the constructor fails you should throw an exception to make sure that you do not have an invalid object.

Design:

RBNode* nil;


Is this supposed to represent a NULL pointer?

If so prefer NULL. Or if you are using C++11 or later then use nullptr.

Usage

void RBTree::RBinsert(T data) {
    RBNode* x;
    RBNode* y;


Declare variables where they are used. Declaring variables at the top of the function is a C style. In C++ you declare variables at the point they are being used (or as close to that point as possible).

Two line initialization?

RBNode* z = new RBNode();
z->data = data;


Your constructor for a RBNode can be passed the data object and allow one line initialization. Reading further into the function I would go even further. This whole function can be simplified by moving the node initialization into its construtor of RBNode.

void RBTree::RBinsert(T data)
{
    RBNode* temp = RBTree::search(data);

    if(temp != RBTree::nil) {
        cout * y = this->RBTree::nil;
    RBNode* x = this->root;

    while(x != RBTree::nil) {
        y = x;
        x = (data data) ? x->left : x->right;
    }

    RBNode* z = new RBNode(data, y); // Node knows to set itself to RED if
                                           // y is not NULL Green otherwise
    if(y == RBTree::RBTree::nil) {
        root = z;
    } else {
        RBTree::RBinsertFixup(z);          // Only re-balance if not the first node
    }
}


I forget how to do the rotation for a Red/Black tree (that's why I have a copy of Knuth). But it looks fine.

Code Snippets

using std::ifstream;
void RBwrite(RBNode<T>*);
friend std::ostream& operator<<(std::ostream& stream, RBTree<T> const& data)
{
    // write tree to stream
    // Internally you can call RBWrite() but passing a stream
    // I would make RBWrite private
}
friend std::istream& operator>>(std::istream& stream, RBTree<T>& data)
{
    // read tree from stream
    // Internally you can call RBRead() but passing a stream
    // I would make RBRead private
}
bool treeCreated = false;

Context

StackExchange Code Review Q#31166, answer score: 6

Revisions (0)

No revisions yet.