1

I'm having trouble implementing the CLRS method for a Red Black Tree. I'm not able to successful insert and I don't know what I'm doing wrong. I struggle with pointers and it's a topic I'm trying to learn more about (I'm buying a book on Amazon this weekend to learn more). Please help me pin point errors. I'm not receiving error messages, but my code won't run past my RBTInsert function, so I know it's getting stuck there. I call RBT Insert in another function that's not shown, but I didn't show it because I'm convinced my errors are in my pointers and nodes.

enum Color {RED, BLACK};

template <typename T>
struct Node
{
    T data;
    bool color;
    Node<T>* left = nullptr;
    Node<T>* right = nullptr;
    Node<T>* p = nullptr; //p is parent
}


template <typename T>
class RBT
{
private:
    Node<T>* root;
    
    void RotateLeft(Node<T>*, Node<T>*);
    void RotateRight(Node<T>*, Node<T>*);
    void RBTFixUp(Node<T>*, Node<T>*);
    void MakeEmpty(Node<T>* root);


public:
    RBT(): root(nullptr){}
    ~RBT();
    void RBTInsert(Node<T>* &root, T data);

};



template <typename T>
void RBT<T>::MakeEmpty(Node<T>* root)
{
    while (root != nullptr)
    {
        delete root;
        MakeEmpty(root->left);
        MakeEmpty(root->right);
    }
}



template <typename T>
RBT<T>::~RBT()
{
    MakeEmpty(root);
}




template <typename T>
void RBT<T>::RotateLeft(Node<T>* root, Node<T>* x)
{
    Node<T>* y = x->right;

    x->right = y->left;

    if (y->left != nullptr)
        y->left->p = x;

    y->p = x->p;

    if (x->p == nullptr)
        root = y;
    else if (x == x->p->left)
        x->p->left = y;
    else
        x->p->right = y;

    y->left = x;
    x->p = y;
}




template <typename T>
void RBT<T>::RotateRight(Node<T>* root, Node<T>* x)
{
    Node<T>* y = x->left;

    x->left = y->right;

    if (y->right != nullptr)
        y->right->p = x;

    y->p = x->p;

    if (x->p == nullptr)
        root = y;
    else if (x == x->p->right)
        x->p->right = y;
    else
        x->p->left = y;

    y->right= x;
    x->p = y;
}




template <typename T>
void RBT<T>::RBTFixUp(Node<T>* root, Node<T>* z)
{
    while (z->p->color == RED)
    {
        if (z->p == z->p->p->left)
        {
            Node<T>* y = z->p->p->right;
            if(y->color == RED)
            {
                z->p->color = BLACK;
                y->color = BLACK;
                z->p->p->color = RED;
                z = z->p->p;
            }else if (z == z->p->right)
            {
                z = z->p;
                RotateLeft(root,z);
            }
        }else
        {
            Node<T>* y = z->p->p->left;
            if(y->color == RED)
            {
                z->p->color = BLACK;
                y->color = BLACK;
                z->p->p->color = RED;
                z = z->p->p;
            }else if (z == z->p->left)
            {
                z = z->p;
                RotateRight(root,z);
            }
        }

    }
}


template <typename T>
void RBT<T>::RBTInsert(Node<T>* &root, T data)
{
     Node<T>* z = nullptr;
     z->data = data;

     Node<T>* y = nullptr;
     Node<T>* x = nullptr;

     while (x != nullptr)
     {
        y = x;
        if (z->data < x->data)
            x = x->left;
        else
            x = x->right;
     }

    z->p = y;
    if (y == nullptr)
        root = z;
    else if (z->data < y->data)
        y->left = z;
    else
        y->right = z;

    z->left = nullptr;
    z->right = nullptr;
    z->color = RED;

    RBTFixUp(root,z);

}
mak95
  • 21
  • 5
  • 1
    `Node* z = nullptr; z->data = data;` This is wrong, do you know what `nullptr` is for? Have you ever used [`new`](https://en.cppreference.com/w/cpp/language/new)? – n. m. could be an AI Nov 13 '21 at 06:42
  • Hi, thanks for your response, I used nullptr because I saw some other implementations use it but I’m not sure exactly when it’s appropriate. I used it as a way to initialize the pointer to something that wasn’t dangerous, but if you have more feedback I’d love to learn. My Professor wants us to use nil nodes for simplifying our code, but I’m not sure if that’s the same as nullptr. — In regards to the new keyword, I thought I needed to use that too, but again some other implementations confused me so I tried this way. @n.1.8e9-where's-my-sharem. – mak95 Nov 13 '21 at 07:12
  • You definitely need a C++ textbook. Trying to learn from random code examples on the internet is counterproductive. `nullptr` is a pointer that does not point to anything. You need to know exactly what it means, "something that is not dangerous" is not an exact description of anything. – n. m. could be an AI Nov 13 '21 at 07:18
  • I’m also a bit confused on where to use pointers vs where to return by reference in my program. I’m very new to programming (just switched from working in healthcare) so any help you can offer I’d be very grateful. – mak95 Nov 13 '21 at 07:18
  • Or let me ask a more clear question: can I use return by reference with pointers the same way I use it for any other type of variable, or are there are special considerations with pointers? – mak95 Nov 13 '21 at 07:26
  • I strongly recommend getting a C++ book from the recommended list https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list and going through exercises. The subject is too broad to explain in comments. – n. m. could be an AI Nov 13 '21 at 08:30
  • Your MakeEmpty is wrong. If you delete the root, memory is reclaimed. To then recursively call MakeEmpty() on root->left is wrong as the previous delete means you can no longer use the "->" operator. Further you have this in a while loop. That means you are deleting root more than once. That is an error. This should be an if statement and deletion of root is last . The parameter should be called something different from "root" as you use that in your class, maybe currnode. – SJHowe Nov 16 '21 at 23:09
  • The basic thing with pointers is they have to point to a real object if your read/write to what is pointed to. So if root is NULL, that is an invalid pointer. You cannot just do root->left = something; as you would be trying to derefence NULL and write to the left member. You can do root = new Node; as new creates an object on the heap – SJHowe Nov 16 '21 at 23:18
  • @SJHowe Thank you for your time and thoughtful responses. I've heard that NIL nodes are necessary where I have null, and now I understand why after reading your explanation. What exactly is a NIL node though? Is it just a new node that I name NIL? Like a space filler for lack of better words? – mak95 Nov 18 '21 at 01:05
  • a NIL node will occur for leaf node. If you insert a new node, it will alway be a leaf node at the bottom a tree. It's left and right child pointers will point to nothing (and if they do, their colour is automatically black). In C that is NULL, in C++ that is nullptr. Those are NIL nodes in Red-Black tree parlance. In short, this new node is a leaf node with no children. – SJHowe Nov 23 '21 at 15:30
  • @SJHowe thank you, so if I'm understanding correctly I can use it in place of nullptr and it will have the same affect? – mak95 Nov 24 '21 at 01:46
  • 1
    No. Both NULL in C and nullptr in C++ are the same thing in essence. They mean an invalid pointer. Any pointer can be invalid. And a pointer can point to valid object on the stack or heap, If pointing to a valid object you can read & write to it (providing not const). But a leaf node with NIL child pointers in conceptual but it is likely to be such that child pointers are the same as C/C++ pointers and therefore NULL/nullptr can be used. – SJHowe Nov 24 '21 at 16:11

0 Answers0