I am learning C++ and to do so, I'm implementing some common data structures.
I started implementing a Binary Search Tree and it went all right, so now I'm implementing a Red Black Tree because I want to work through extending the BST Nodes into RBT Nodes (by just adding a bool color to them), and here is when my problems come.
This is my Node definition:
// NODE (key, value) //
class Node {
public:
int key;
int value; // value is integer for simplicity
Node *parent;
Node *left;
Node *right;
Node();
Node(int key, int value);
Node *grandparent();
Node *uncle();
};
And then I have my RedBlackNode:
// RED BLACK NODE (node with color) //
class RedBlackNode : public Node {
public:
int key;
int value;
RedBlackNode *parent;
RedBlackNode *left;
RedBlackNode *right;
bool color;
RedBlackNode();
RedBlackNode(int key, int value);
RedBlackNode *grandparent();
RedBlackNode *uncle();
};
That looks exactly the same, except for the color, and because grandparent() and uncle() return a RedBlackNode and not a Node.
The same happens with my BinarySearchTree methods Insert(key, value) and Search(key), and I would like to know if I have to rewrite all the methods only because of that.
I've been thinking also in using templates, but that doesn't make sense to me, because a BST will only be formed by Nodes, and a RBT will be formed by colored Nodes.
Thanks for your time in advance.
Related
I am studying generic binary search trees (BST) and AVL trees (AVL) on some notes that contain implementation pseudocodes. I am a bit puzzled about some details of their implementation.
The BST is based on the struct Node below
struct Node{
int key;
Node* parent;
Node* left;
Node* right;
//constructors
}
//methods
The AVL version is basically the same with a few fields more for balancing the tree (I'll call it AVLNode for clarity, but there's no such distinction on the notes):
struct AVLNode{
int key;
int height;
int size;
AVLNode* parent;
AVLNode* leftchild;
AVLNode* rightchild;
//constructors
}
//methods
A lot of operations are the same between the two trees and I can easily use templates in order to reuse them on both trees. However, consider the operation insert, which inserts a new node. The code for a BST is something like
//Insert node with key k in tree with root R
void insert(const int& k, Node* root){
Node* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->leftchild=new Node(k,N); //inserts as a left child
else
N->rightchild=new Node(k,N); //inserts as a right child
}
Now, the point is that the insert operation of an AVL tree is basically the same. The pseudocode presented in the notes is as follows:
void avlInsert(int k, AVLNode* R){
insert(k,R); //same operations as for Nodes, shown above
AVLNode* N=find(x,R); //find node inserted (generic operation for BST)
rebalance(N); //perform balancing operations specific to AVL trees
}
I'm a bit puzzled at this point, I know that the above is just a pseudocode but I was wondering whether there is a way to reuse the operation insert already provided for Node. Using template specialization would just mean writing a different specialization insert<AVLNode> for AVLNode, so that's not what I'm referring to.
I think a way would be to define AVLNode as a child class of Node and then use something like
struct AVLNode : Node {
//implementation
}
void avlInsert(int k, AVLNode* R){
Node *root=R;
insert(k,root);
AVLNode* N=find(x,R);
rebalance(N);
}
but I'm not quite sure this would work and I don't know how to manage the pointers to parent and the childs (i.e. they must be pointers to Node inside Node and to AVLNode inside AVLNode).
Is there a way to avoid rewriting the same code?
You could use CRTP here. This would allow you to create the left right and parent nodes in the baseclass. For example consider something like this:
template<typename T>
struct BaseNode{
int key;
T* parent;
T* left;
T* right;
};
struct AVLNode : public BaseNode<AVLNode>{
int height;
int size;
AVLNode(const int&k, AVLNode*root){};
AVLNode(){};
};
struct Node : public BaseNode<Node>{
Node(const int&k, Node*root){};
Node(){};
};
template<typename T>
T* find(const int& k, T* root){return root;};
template<typename T>
void insert(const int& k, T* root){
T* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->left=new T(k,N); //inserts as a left child
else
N->right=new T(k,N); //inserts as a right child
}
void test(){
AVLNode avl_root;
Node node_root;
insert(42, &avl_root);
insert(42, &node_root);
}
The downside is that the compiler will generate more code than necessary. Because it creates a new insert function for every type. This might not be a problem for you, but something worth considering. See godbolt for the generated code.
As an aside. Please please please please don't use raw pointers and new and delete. You'll be going to get so many memory leaks, especially if a pointer gets "lost" because its parent gets deleted. Consider using smart pointers like unique_ptr or shared_ptr
So, the is the problem statement where I am stuck and trying to seek help for it is the following: As a part of my assignment, we have been asked to print the levels of the all the non-leaf nodes of a Binary Search Tree. We have been the function's template:
int Print_Non_leaf_Level(BST *Root)
And below is the memory of an object of the following class acts as the node of a linked list representing Binary Search Tree of integers.
class BST{
private: int info;
BST *left;
BST *right;
public: BST () {} //NULL Constructor.
};
Following is what I tried to do, but I am not sure if my logic is correct.
int Print_Non_leaf_Level(BST *Root){
BST *temp;
int level=-1;
temp = Root;
if(temp!=null){
if(temp->left !=null || temp->right != null){
++level;
cout<<level<<'\n';
}
Print_Non_leaf_Level(temp->left);
Print_Non_leaf_Level(temp->right);
return level;
}
}
I ran into this bit of code for a c++ linked list implementation.
struct node
{
int info;
struct node *next;
}*start;
What does this mean to have *start rather than just start??
What happens when it is later used like this? What does s mean it is not referenced anywhere else in the function?
struct node *temp, *s;
temp = new(struct node);
Fragment
struct node
{
int info;
struct node *next;
}*start;
is equivalent to
struct node
{
int info;
struct node *next;
};
struct node *start;
So in your first fragment, you define a structure named node and a variable named start of type struct node * within one statement. That's all.
Note that in C++ (unlike in C), you could also write
struct node
{
int info;
node *next;
};
node *start;
i.e. you can omit the struct-keyword when defining variables of type struct node.
I have made a superclass named "tree". I have constructed the tree in this class. Now, I want to pass the root of the constructed tree to another class which is a subclass of tree. But when I try to pass it, the subclass calls the supercalss constructor and sets it to NULL;
struct node
{
struct node *left;
struct node *right;
int val;
};
struct node *create(int val)
{
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->val = val;
temp->left = temp->right = NULL;
return temp;
};
class tree
{
public:
struct node *root;
tree()
{
root = NULL;
}
void createtree()
{
root = create(5);
}
void preorder()
{
preorderp(root);
}
void preorderp(struct node *p)
{
if(!p) {
return;
}
cout<<p->val<<' ';
preorderp(p->left);
preorderp(p->right);
}
};
This is the definition of my tree class. It just creates a tree with one node having value 5. Now I want to pass the new root created to a subclass of tree.
class treeiterator:public tree
{
struct node *p;
stack<struct node *> s;
public:
treeiterator()
{
p = root;
push(root);
}
bool hasnext();
int next();
private:
void push(struct node *root);
};
I create an object for tree first and then do createtree. Now, when I create an object for treeiterator, it's member p gets sets to NULL since supercalss constructor is also called. How can I just access the tree created in the superclass in subclass?
Full code:
#include <bits/stdc++.h>
using namespace std;
struct node
{
struct node *left;
struct node *right;
int val;
};
struct node *create(int val)
{
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->val = val;
temp->left = temp->right = NULL;
return temp;
};
class tree
{
public:
struct node *root;
tree()
{
root = NULL;
}
void createtree()
{
root = create(5);
}
void preorder()
{
preorderp(root);
}
void preorderp(struct node *p)
{
if(!p) {
return;
}
cout<<p->val<<' ';
preorderp(p->left);
preorderp(p->right);
}
};
class treeiterator:public tree
{
struct node *p;
stack<struct node *> s;
public:
treeiterator()
{
p = root;
push(root);
}
bool hasnext();
int next();
private:
void push(struct node *root);
};
void treeiterator::push(struct node *t)
{
while(t) {
s.push(t);
t = t->left;
}
}
bool treeiterator::hasnext()
{
return s.empty()?1:0;
}
int treeiterator::next()
{
struct node *t = s.top();
int val = t->val;
s.pop();
if(t->right) {
push(t->right);
}
return val;
}
int main()
{
tree t;
t.createtree();
t.preorder();
treeiterator it;
while(it.hasnext()) {
cout<<it.next()<<' ';
}
}
Because of inheritance every treeiterator is also a tree. This means
treeiterator treeIt;
treeIt.createtree();
will do what OP wants. There is no need to make a separate tree and moving the root around.
However this is a bit odd in the world of C++ because OP is under-using the constructor. For example, node could be:
struct node
{
node *left;
node *right;
int val;
node(int inval):
val(inval),
left(nullptr),
right(nullptr)
// the above is a Member Initializer List. It makes sure all of your
// members are initialized before the body of the constructor runs.
{
}
};
That bit after the : in the constructor is the Member Initializer List.
Now when you allocate a node it's initialized and ready to be linked. For tree
class tree
{
public:
struct node *root; // almost certainly should not be public.
// also should be a std::unique_ptr<node>
tree(int inval)
{
root = new node(5); // note new in place of malloc. new allocates
// storage and calls constructors. malloc should
// only be used in C++ in rare edge-cases.
}
/* obsolete
void createtree()
{
root = create(5);
}
*/
...
};
tree is assigned a root on allocation. treeiterator is a wee bit trickier because it must call tree's constructor to set up root.
class treeiterator:public tree
{
struct node *p; // Don't see the point off this
stack<struct node *> s; // or this, but that's another question
public:
treeiterator(int inval):
tree(inval) // call's tree's constructor
{
}
bool hasnext();
int next();
private:
void push(struct node *root);
};
Allocating a treeiterator now guarantees that it is all ready to go with no further work.
treeiterator treeIt(5); // all done.
All of the above is covered within the first few chapters of any good C++ programming text. I recommend getting one and reading it, because right now it looks like you are trying to write bad C.
Off topic 1:
You are going to quickly find that this code is in violation of the Rule Of Three. What is The Rule of Three? If you don't know, read the link. It will save you much time and hair-pulling
Off Topic 2:
#include <bits/stdc++.h>
using namespace std;
Is a ticking time bomb. The first line includes the entire standard library, but only in GCC. Your code is now doing far, far more work than it need to to compile, is no longer standard C++, and is not portable to other compilers and may well break with the next revision of GCC. Don't use anything in bits. It internal compiler-specific stuff with no guarantees what-so-ever.
More here: Why should I not #include <bits/stdc++.h>?
The second line takes everything in the std namespace and places it in the global namespace. This leads to fun games like is reverse or std::reverse being called? Often this leads to insane and arcane compiler messages because the poor compiler is confused as hell, but sometimes it's not confused and picks the best choice among the many and silently breaks something else. Great fun debugging.
More here: Why is "using namespace std" considered bad practice?
Together you have the entire standard library pulled into your file AND stripped of it's proper namespace. This results in a vast minefield of potential hidden pain that is not worth any perceived time savings. One of the resulting bugs could cost more clean up than years of typing a few extra lines per file and characters.
No one want to clean up code with this stupid a mistake, so doing this in a professional setting can be costly.
First, you should not have root has public. This is a gross OO error. If you want it to be available to subclasses you should make it protected.
struct node
{
int data;
struct node *left;
struct node *right;
};
///////////////////
struct node
{
int data;
node *left;
node *right;
};
I am unable to figure what's the difference between these two implementations. Thanks in advance for help.
There isn't any difference in C++.
In C a struct tag is not usable as the name of a type (although you can typedef a name the same as the tag and use the typedef). In C++ you don't have to go through that palaver. The first form is only allowed for backwards compatibility with C.