Add nodes to binary search tree c++ - c++

I am building a binary search tree. Now I am having a problem with adding a node to the tree.
void BinaryTree::add(int value, Node* node) {
if(!node)
node = new Node(value);
else if(node->key < value)
this->add(value, node->rightNode);
else if(node->key > value)
this->add(value, node->leftNode);
}
This code does not seem to work when I call:
BinaryTree test;
test.add(4, test.root);
test.add(1, test.root);
test.add(5, test.root);
test.add(2, test.root);
test.add(3, test.root);
test.add(7, test.root);
test.add(6, test.root);
After the first add call, the root of the tree 'test' is still empty.
How shall I change the code so that it will be updated when I call add and the node goes to the correct place of the tree?
Thank you very much!

You are passing the Node * by value here:
void BinaryTree::add(int value, Node* node) {
One solution is to pass by reference instead:
void BinaryTree::add(int value, Node *& node) {
^
If you pass by value the function is just receiving a copy of the Node * and so any modification to it will not be reflected back in the calling code.
Also you might want to think about what happens when value is equal to the key.

You recursively call the add function, but nowhere in there do I see you actually assigning leftNode or rightNode to the passed in node.

Related

Why Is My Binary Tree Overwriting The Leaves Of Its Root?

I've pinpointed my issue to this specific function, it's the helper function for my binary tree. Before this function call there is a node but instead of growing it seemingly just replaces that node. When I look at my code in my head it all makes sense but I can't figure out what I'm doing wrong.
Here is the function that calls add:
void BSTree::Insert(Client &newClient) {
if (isEmpty())
{
Node *newNode = new Node(newClient);
this->root = newNode;
}
else
add(this->root, newClient);
}
and here is my add() function:
BSTree::Node* BSTree::add(Node *node, Client &newClient) // helper function for Insert()
{
if (node == nullptr)
{
Node *newNode = new Node(newClient);
//node = newNode; // already tried adding this in
return newNode;
}
if (newClient.clientID < node->pClient->clientID)
return node->left = add(node->left, newClient); // already tried just returning add()
else
return node->right = add(node->right, newClient);
}
Since this is your question, I will explain what your code is doing. Imagine you have a mature binary tree already and you are adding a node to your tree. By the time you reach this line
return node->left = add(node->left, newClient);
Three separate instructions are carried out:
newClient is added to the left branch of node by add().
the left child of node is set to the return value of add().
the right hand side (RHS) of the assignment is returned by the parent function.
The issue is with number 2. If the tree you are adding to is mature already, changing left child of nodes as you're traversing the tree will cause the override effect that you're observing. In fact, the problem goes beyond overwriting leaves. Since you use the new keyword, the overwritten nodes still have allocated heap space, are never deleted and cause a memory leak.
Here are some thoughts to get you on the right direction:
Your insert() function ensures that the first time you call add(), you are not passing nullptr as the first argument. Take advantage of that and ensure nullptr is never passed into add() function by checking for nullptr before you do the recursive call. Change the return type of add() to void. You no longer need to check node is nullptr. Here's some pseudocode to guide you
void add(node, val)
if val < node.val
if node.left exists
add(node.left, val)
else
make a new object and set node.left to that object
else
if node.right exists
add(node.right, val)
else
make a new object and set node.right to that object
There is a problem with your logic. First of all, there is the insert() method which you should write like this for better understanding:
void BSTree::Insert(const Client &newClient) // use const to prevent modification
{
if (isEmpty()) { root = new Node(newClient); }
else { add(this->root, newClient); }
}
This way you are creating a new object at root directly with the help of 'root' pointer in BSTree.
Now, about the add() method. The 'node' you are passing as a parameter is a copy of the pointer variable, so the actual pointer value is not changed. See this:
BSTree::Node* BSTree::add(Node *node, Client &newClient) //logical error
You need to pass the Node* by reference like this using 'Node* &node':
BSTree::Node* BSTree::add(Node* &node, const Client &newClient)
Why is you binary tree overwriting the roots of its leaves? Answer:
Your recursive call with return statement is totally wrong.
return node->left = add(node->left, newClient);
The add(node->left, newClient) always returns the address of the leaves, and you are returning this value. It goes for recursive calls until it reaches the leaves place.
Conclusion: Since, there are a lot of bugs, I would suggest you re-write logic again carefully.
I hope this helps! :-)

Binary tree recursive insertion method doesn't work

void insert(int key)
{
insertRec(key, root);
}
void insertRec(int key, Node *current)
{
if(current==NULL)
current = new Node(key);
else if(key <= current->value)
insertRec(key, current->leftChild);
else
insertRec(key, current->rightChild);
}
How come this doesn't work?
In the insert function, key value and root of the tree are passed to insertRec. If the node is null, create a new node and set it to the key value. Else, either recursively go left or right until the node hits a null spot and insert a new node there.
It's not true the code 'doesn't work'. Of course it works, just as it is written. The problem is you wrote something different from what you need.
when you pass an argument to insertRec(), the routine gets a copy of a pointer to the Node object. So when you assign a value in
current = new Node(key);
you overwrite a local copy in the local variable current. The calling function (and its data) do not know about it.
If you want the caller to recieve the new value, declare a function takes a reference to a variable:
void insertRec(int key, Node *&current)

Binary tree rotate function without reference-to-pointer, pointer-to-pointer, or the function returning the new node

I have to make an implementation of the following class in C++:
struct Node {
Node* parent;
Node* left;
Node* right;
std::string name;
};
class BinaryTree {
public:
BinaryTree();
std::string ToString() const;
void RotateLeft(Node* node);
void RotateRight(Node* node);
void MakePerfect();
private:
void CreateVine();
void MakePerfectFromVine();
private:
Node *root;
};
This class implements the DSW algorithm for making a Binary tree perfect (all levels are full, except the last one)
I have done an implementation which works, I tested it and everything seem fine but I had to change the rotate functions thus:
void RotateLeft(Node*& node);
void RotateRight(Node*& node);
Unfortunately the person who set me this task said that I'm forbidden to change them. But the unaltered functions don't work. I use them in CreateVine() and MakePerfectFromVine():
void CreateVine() {
Node *current = root;
while(current != NULL)
if(current->left != NULL){
current=current->left;
rotateRight(current);
}
else
current = current->right;
}
In this fragment the pointer current points to a node in the tree. After the node is rotated, it will have changed its parent and/or left and/or right child, but current will point to the old node with the old information. In order for the changes made in the rotate function to be reflected outside of the rotate function, I have to use reference-to-pointer, pointer-to-pointer or the function itself has to return the node;
The only way I can think of to fix this problem is if I know that the binary tree doesn't allow duplicate elements, after I rotate the node I search for its string value and set current to point to it, but I'm not sure if the binary tree doesn't allow duplicate elements and that would increase the time complexity of the algorithm.
Is there any way of avoiding the use of reference-to-pointer and pointer-to-pointer?
Excluding the root of the tree, for which you might have to think something else, the pointer to a given node inside the tree can be obtained with an extra level of indirection:
Node **pp;
if (node->parent->left == node)
pp = &node->parent->left;
else
pp = &node->parent->right;
So you can actually maintain your algorithm and just resolve the reference doing this inside the function. Note that this is a sketch, you will have to check for the root (parent==0) and operate differently there

Tree Construction with pointers

I'm trying to create a function that inserts a key struct into a tree. The function sets the root correctly, but does not set the branches when called again with another key. Here is the code:
tree.h:
class tree{
key *tree_root;
public:
tree();
//Constructor
void treedestroy(key *root);
//Tree destructor helper
~tree();
//Destructor
void insert(key* root, key *newkey, int disc);
};
insert function from tree class:
void tree::insert(key *root, key *newkey, int disc){
if (root == NULL){
root = newkey;
return;
}
if (newkey->cord[disc] <= root->cord[disc])
insert(root->left, newkey, (disc+1)%4);
else if (newkey->cord[disc] > root->cord[disc])
insert(root->right, newkey, (disc+1)%4);
}
I'm a little inexperienced with C++ pointers and was wondering how I could fix this code so that it will fill the tree correctly?
I'm not entirely sure about your approach here, but to help get you on your feet, it would help to use a function signature of:
void insert(key*& root, key *newkey, int disc);
This passes the root pointer by reference, which means that changes made inside the function will "stick" to the variable you passed in.
Your function as it stands modifies a function-local variable, without those changes propagating.
This article is a balanced and quick read on passing by reference (I can't say if it's the best - it was just the first decent one I found)
If on the first call newkey is null, root will stay null. Make sure method call is correct.
I would put an else rather than an else if. If it is a binary tree it is either equal, greater, or less than.
Does it get into Insert_helper or not? Why did you not include it, seems important? I would guess it is getting at least that far.
root = newKey;
This do not modify actual root. It just modify function argument which is a copy of pointer you specify when call intsert function.
Correct version would looks something like this:
private:
void tree::insert_helper( key **root, key *newkey, int disc ) {
if ( (*root) == NULL ) {
*root = key;
} else if (newkey->cord[disc] <= root->cord[disc]) {
insert_helper( &((*root)->left), newkey, (disc+1)%4 );
} else {
insert_helper( &((*root)->right), newkey, (disc+1)%4);
}
}
public:
void tree::insert( key *newKey, int disc ) {
insert_helper( &tree_root, newkey, disc );
}
Also you have to be sure that 'key' constructol set NULL for left and right. And tree constructor should set NULL for tree_root

Binary Tree search returns no results (C++)

I am working on some binary tree algorithms and need a "find node with searchindex..." function. The design for treenodes is basically
class TreeNode {
int index; // some identifier
TreeNode *left;
TreeNode *right;
}
and a tree is defined by a pointer to the root-node.
My implementation for the search function is:
void Tree::searchNode(TreeNode * root, int nodeIndex, TreeNode *resultNode){
/* Recursive search */
if (root->index == nodeIndex) {
resultNode = root;
} else {
/* search children if the current node is not a leaf */
if(!root->isLeaf()) {
this->searchNode(root->left,nodeIndex,resultNode);
this->searchNode(root->right,nodeIndex,resultNode);
}
}
}
Arguments: *root is the root-node of the tree, nodeIndex is the search-index and *resultNode is the pointer to the found (or not) node in the tree.
The function does not return a reference or pointer to the found node but modifies the pointer resultNode so it points to the found node. The idea is to initialize resultNode with NULL, perform the search and modify it if a match occurs. Otherwise it remains NULL and I can easily check if there are search results or not.
Another class with a tree buildingTree as member utilizes the search-function in this way:
TreeNode *resultNodePtr = NULL;
this->buildingTree->searchNode(this->buildingTree->rootPtr,
currentNodeIndex, resultNodePtr);
// do sth. with resultNodePtr if != NULL
I create *resultNodePtr on the stack because I just need it temporarily inside the function. Is this done correctly? However: The function does not work. resultNodePtr is always NULL, even if the tree contains a node with the search-index. I debugged it very carefully step by step, it detects
(root->index == nodeIndex)
correctly but
resultNode = root;
does not work (I want resultNode to point to the same adress root points to).
Debugger says resultNode before assignment is 0x0, root node is some adress, after the assignment resultNode remains 0x0.
Do I have to overload the operator= in this case for the class TreeNode?
I have tried it:
TreeNode & TreeNode::operator=(const TreeNode & oldTreeNode){
*this = oldTreeNode;
return *this;
// ignore childs for now
}
I am not an expert but this operator= seems trivial. Does it affect the assignment of two TreeNode pointers *node1 = *node2 at all?
Maybe you can help me. Thanks for reading, appreciate your help.
If I find a solution myself I will post it here.
Regards,
Mark
Because you pass resultNode into the function as a pointer by value, its original value never changes. Think of TreeNode* as literally nothing more than a number representing a memory address; when you reassign it:
resultNode = root;
This modifies the copy that searchNode has, but not the original pointer in the code which invokes searchNode. Take this simpler example:
void Foo(int x)
{
x = 100;
}
void Bar()
{
int x = 0;
Foo(x);
// at this point, x is still 0
}
resultNode's value doesn't change from NULL for the same reason that x doesn't change from 0 when the function Bar is invoked. To fix this issue, pass the pointer in as a pointer to a pointer, or a pointer by reference:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode*& resultNode)
{
// same code
}
... or:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode** resultNodePtr)
{
// assign to *resultNodePtr instead
}
Your resultNode pointer is being passed by value, not by reference. So when the function call completes the pointer on the calling side does not receive a value.
Your algorithm looks fine :)