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 :)
Related
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.
I am studying C++ from Herbert Schildt's book "Teach Yourself C++ 3rd Edition". In one example code, something made me so confused.
#include <iostream>
using namesapce std;
template <class data_t> class list {
data_t data;
list *next;
public:
list(data_t d);
void add(list *node) {node->next = this; next = 0; }
list *getnext() { return next;}
data_t getdata() { return data;}
};
I didn't write all code in example, I only writed class declaration.
In here, I didn't understand one part. First, the "*node" pointer which belongs to "add" function, is using as "node->next" for "*next" pointer. I didn't understand the aim in here, why we don't directly use like {"next=this; next=0;} ? Also, how can we use an empty pointer (*node) to point another empty pointer (*next) ? I am missing maybe some key concepts, thanks for helps.
In the function add():
template<typename data_t>
class list {
// ...
void add(list *node) {node->next = this; next = 0; }
// ...
};
The point of the function is to add this to the list, as the next element after node. Think about it like a train: add() tells the current car to hook itself onto the back of another car, with the function's parameter telling it which car to link to.
Note that this function expects node to not be a null pointer; if it is null, you'll probably get a segmentation fault.
void add(list* node) {
node->next = this; // Tells "node" to register the current node as its "next" node.
// Should first check whether "node" is a valid pointer.
next = 0; // Tells the current node to register a null pointer as its "next" node,
// signifying that it's currently the last node in the list.
// Note that instead of assigning 0, it should instead assign either
// "nullptr" (C++11 or later) or "NULL" (pre-C++11).
}
Therefore, when used like this:
list<int> start(42), second(24);
second.add(&start);
It will do the following:
(&start)->next = &second;
(&second)->next = 0;
This creates a singly linked list, which looks like this:
Start: "start"
--> Next: "second"
--> Next: None.
Or, more concisely:
start->second->NULL
Remember, when inside a non-static member function, any member variable accesses are assumed to operate on this unless otherwise specified.
template<typename data_t>
void list<data_t>::add(list* node) {
node->next = this; // Operates on node, not on this.
next = 0; // Operates on this; becomes "this->next = 0;"
}
If this function intstead was just {next = this; next = 0;}, then it would be:
template<typename data_t>
void list<data_t>::add(list *node) {
this->next = this; // Bad. Creates a circular linked list, where "this" is ALWAYS the
// next node after "this".
this->next = 0; // Renders the above line entirely pointless, by indicating that there
// are no nodes after "this".
}
I was trying to implement BST using C++ , so i tried this:
#include <iostream>
#include <stdlib.h>
struct node
{
int value;
node* left;
node* right;
};
void insert(node *cur , int val)
{
if(!cur)
{
cur = new node;
cur->value = val;
cur->left = NULL;
cur->right = NULL;
return;
}
if(val <= cur->value)
insert(cur->left , val);
else
insert(cur->right , val);
}
using namespace std;
int main()
{
node *root = NULL;
insert(root , 20);
insert(root , 21);
cout<<(*root).value;
return 0;
}
but I have a problem, my insert() function works good, but the change in cur does not seem to reflect into the root pointer, as root remains NULL after the `insert() function calls. What is wrong here?
EDIT: Thanks for all your answers, making a pointer to a pointer seems to be to be ugly and tedious, is there any other way around, acheiving this with some other design?
Here, the root itself has been passed to insert() using pass-by-value. so, from insert(), the value of root cannot be changed. In other words, the cur is local to insert() function. Any changes made to cur itself won't impact the actual argument passed.
If you want to change the value of root from insert(), you need to pass a pointer to root from main().
To elabotare, you can change the value at the address pointed by cur from insert(). So, following the same analogy, if you change
insert(&root , 20);
void insert(node **cur , int val)
all the occurrences of cur to *cur
you should be all good to go.
If you want the function to operate on the outside pointer rather than a local copy you need to pass by reference:
void insert(node*& cur, int val)
{
// ...
}
Otherwise the function works on a copy of the pointer and the outside variable remains unchanged.
The wrong is that you pas a pointer by value, change that value but the caller does not know about it. Change it to
void insert(node **cur , int val)
{
if(!*cur)
{
*cur = new node;
(*cur)->value = val;
(*cur)->left = NULL;
(*cur)->right = NULL;
return;
}
if(val <= (*cur)->value)
insert((*cur)->left , val);
else
insert((*cur)->right , val);
}
And change function call accordingly (...exercise!)
C++ makes function calls as Call by Value. So it makes a copy of the pointer and passes that to the function. If you pass that pointer, you have access to the data the pointer is pointing to, but NOT to the pointer outside the function itself, as only the adress the pointer is pointing to is copied.
You need to pass a pointer to the pointer if you want to modify the pointer itself (that would be the C attempt) or pass by reference, of which c++ is capable of.
If you want to use the C attempt:
void insert(node ** cur , int val)
if(!(*cur))
{
(*cur) = new node;
(*cur)->value = val;
(*cur)->left = NULL;
(*cur)->right = NULL;
return;
}
or the C++ attempt (here you only have to modify the type of cur, everthing else will remain as it is):
void insert(node *& cur , int val)
If you reassign cur to a new node in insert, that does not mean that root is assigned that value (especially that root is not an address at all, but NULL).
Either pass a pointer to an empty node to insert on initialization (and update it with relevant data) or return a new node (and assign it to root in main).
I am learning C++
I have a function:
void remove_node(node *&rem);
As I understood, rem is a node*, which is passed by reference. I do need it to be passed by reference, because I change the pointer.
But sometimes I need to pass it by value (recursive call). I did it like this:
node *noref = rem->left;
remove_node(noref);
instead of
remove_node(rem->left);
Is there better way to do it?
Thanks.
What do you wish to do with your nodes ?
What are the attributs & methods of the class ?
Usually when using nodes, you just change the links between the nodes, and not the nodes themselves (ex : double-linked list).
struct node {
node* right;
node* left;
};
typedef struct node node;
void remove_node(node* n) {
l_node = n->left;
r_node = n->right;
l_node->right = r_node;
r_node->left = l_node;
free(n);
}
that would be the C code, to pass it to C++ just use the accessors and the destructor :)
This is a noobie question, but I'm not sure how to pass by reference in C++. I have the following class which sets up a Node and a few functions.
class Node
{
public:
Node *next;
int data;
Node(int dat)
{
next = NULL;
data = dat;
}
Node* getNext()
{ return next; }
void setNext(Node *n)
{ next = n;}
void reverse(Node *root)
{
Node *previous = NULL;
while(root != NULL)
{
Node *next = root->getNext();
root->setNext(previous);
previous = root;
root = next;
}
root = previous;
}
};
Now, the purpose of my little class is to create a singularly linked list and have the ability to reverse it. And it seems to work fine, if I return the node named 'previous' at the end of reverse.
But look at my main function:
int main()
{
Node *root = new Node(1);
Node *num2 = new Node(2);
Node *num3 = new Node(3);
Node *num4 = new Node(4);
root->setNext(num2);
num2->setNext(num3);
num3->setNext(num4);
root->printList();
root->reverse(root);
root->printList();
return 0;
}
printList() was omitted for sake of space, but it just prints a list given a node. The problem is, when root->reverse(root) is called, root doesn't actually end up pointing to 'previous'.
The output would be this:
1
2
3
4
// the value of previous from the reverse function is 4
1
I really don't understand the output. Anyone care to explain what's happening? (Why isn't the list reversed even though if I did something like this root = root->reverse(root) where reverse returns previous, it would) why is it that root now only points to itself? I'm new to c++ and appreciate your help!
C++ has support for reference semantics. Therefore, for a given function:
void foo(Bar& bar);
To pass by reference you do:
int main() {
Bar whatsit;
foo(whatsit);
return 0;
}
That's it!
This is commonly confused with passing a pointer, where for a function such as:
void foo(Bar* bar);
You would do:
int main() {
Bar whatisit;
foo(&whatsit);
return 0;
}
The difference is mostly a matter of semantics:
- A reference is always valid. There is no reason to check for a NULL pointer.
- A pointer could be NULL, and as such, should be checked.
It is, however, possible for a reference to refer to a NULL pointer, however, if the programmer decides to be evil and abuse reference semantics, but the principle remains.
You aren't passing by reference. You are passing a copy of the pointer. This copy still points to the same node, but it is still just a copy with local scope. Basically it is another pointer pointing to the node that the pointer in main is pointing to (ha!). At the end of your function, your assignment is assigning previous to this pointer copy, and then the function ends and the copy goes out of scope. Your pointer in main remains unchanged.
The reason returning/assigning the pointer worked is that this copy which has been set to what you want is returned and assigned to your pointer in main.
You can fix this in a multitude of ways. Pass a reference to your pointer (ugly imo), use references, or return root and do an assignment.
To pass a pointer by reference you can declare reverse as:
void reverse(Node*& root) {
// ...
}