binary search tree insertion with argument reference - c++

I wrote the following function to practice binary search tree insertion:
void RecursiveInsert(struct Node* &root, int key) {
if (root == nullptr) {
root = new Node(key);
return;
} else if (root->key > key) {
RecursiveInsert(root->left, key);
} else {
RecursiveInsert(root->right, key);
}
}
It works, but I don't understand why should I pass in a reference to root. Does anybody know why?

You want the function to be able to change the pointer stored in the parent node of the node that you're going to insert so that it points to the new node instead of nullptr. If you don't use pass-by-reference, all the function can do is modify a local copy of the pointer, and the pointer in the actual tree won't get changed.
For example, let's say you're looking at a node, and you want to insert the new node as this node's right child.
someNode[key: 123, left: nullptr, right: nullptr]
The function is going to be called with the right pointer as an argument. You want the function to be able to change the node so it looks like this:
someNode[key: 123, left: nullptr, right: newNode]
If you don't pass-by-reference, the function can't change the right pointer of the node, since it has only been given a copy. Using the reference allows the function to actually modify the pointer stored in the node that was used as the argument.

Related

What's wrong with my avl tree add() function? [duplicate]

Printed out the address of head and &head:
head:0x603050
&head :0x7fffffffe4b8: what does this signify?
void push(node* &head,int key)// Inserts items at front of link list
{
node* linkNode=new node(); //declares new node
linkNode->data=key;
if(head==NULL) //if the link list is empty then create a new one.
{
linkNode->next=NULL;
head=linkNode; //1
}
else
{
linkNode->next=head;
head=linkNode;
}
}
Main function where all other functions are called from
link list is 8,4,2
main function
int main(int argc, char** argv)
{
node* head=NULL; //initializing head to NULL
push(head,2); //creating link list
push(head,4); //this requires &head
push(head,8); //link list is 8,4,2
selectSort(head); //this does not require &head
reverse(head); //this requires &head
return 0;
}
Why do we need to pass it by reference of reference such as in push(node* &head, int key)
Otherwise it won't work to set the given linkNode as current head:
if(head==NULL) //if the link list is empty then create a new one.
{
linkNode->next=NULL;
head=linkNode; // <- This statement changes the head variable passed from main()
}
What you have is a reference to a pointer (head) that will be 'returned' from the push() function, and set the head pointer passed from the caller correctly:
node* head=NULL;
push(head,2); // sets head to the node created for key '2'
Don't forget to delete all the node instances you have created with new node();. In a different context as you're showing, this might lead to memory leaks.
That's not a "reference of reference"; it's a reference to a pointer.
It means that, once the pointer head has been set to point to the new element, within the function, this change also affects the pointer you originally passed in to the function.
selectSort(head); //this does not require &head
It probably should do, actually, if the function performs a sort over all elements of the list.
reverse(head); //this requires &head
After this call, head now points to the new head of the list. This would not be possible if you'd passed head by value.
An alternative implementation might return the new head pointer, instead of using this "out parameter" convention.
The reason you must pass head to the push function is because your push function is expecting to modify the value of the head pointer. If you don't pass it by reference, any changes to it will only be available within the function call. For example, if it was not passed by reference, and you passed head (initialized to NULL), to the push function, a new item would be created, but your value for head would only be updated inside the function. Once you left the function, it will still be NULL (because you passed the pointer by copy).
Note that this can go away if you create a linked list class instead of treating your nodes as a linked list themselves (that is, encapsulate the nodes under a list interface - which is what the standard library does).

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)

Why is "T *&root" being passed for insert operation in a binary tree?

I was creating a custom binary tree and has stumbled in the following code for correct insertion in the tree from this site.
void treeInsert(TreeNode *&root, string newItem) {
if ( root == NULL ) {
root = new TreeNode( newItem );
return;
}
else if ( newItem < root->item ) {
treeInsert( root->left, newItem );
}
else {
treeInsert( root->right, newItem );
}
}
Why do you need to passed TreeNode *&root instead of TreeNode *root?
If you pass a pointer, rather than a reference to a pointer, modifications that you do to that pointer itself would be local to your treeInsert function. This prevents you from inserting the root - the only case when you must modify the pointer itself (the third line in your source code).
For example, if you do this
TreeNode *root = NULL;
treeInsert(root, "hello");
and treeInsert takes TreeNode*, the value of root after the call of treeInsert will remain NULL, because the third line of your source code would modify a local copy of the root pointer.
Consider the case when your newItem is the root item. Then this will change your root element. This means that at the end of the operation, your note 'root' has to point to the newly inserted TreeNode.
Now if you only pass TreeNode *root, you can only change the value, but at the end, your root pointer cannot be changed. So, you have to pass your pointer by reference, so that you can change it within your function.
The pointer TreeNode* is passed by reference, so that the original pointer in the calling function can be changed, in case the tree is empty.
Otherwise, if you just pass a copy of the pointer, the modifications will be local to the function being called.
A comment is present in the code itself: Note that root is passed by reference since its value can change in the case where the tree is empty.
If you don't do this, you have a NULL pointer in the calling function representing the root of the tree, which is clearly wrong since you just inserted an element.
Some other ways of handling the situation are:
Returning the local root pointer
root = treeInsert(root, str);
Passing pointer to root pointer, so it can be changed
treeInsert(&root, str);

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 :)