How to parse a large tree? - c++

recently I passed a programming interview where I had to create a method that returns the address of a node (belonging to a tree). The method takes an integer value as an argument.
My code worked on a small tree, but when searching a large tree (300,000 nodes) I got an error stating "cannot access address '0x.....'".
What should I do to fix this?
'''
struct Node
{
int value;
Node* left = nullptr;
Node* right = nullptr;
Node* find_node(int);
};
Node* Node::find_node(int v)// The function is working on small trees only
{
if(this->value == v) //comparing the the value inside the root with the function's argument
return this;
else if(this->value > v) //if v is smaller than the node's value, search the next left node
{
if(this->left == nullptr) //checking if the next node on the left exists
return nullptr; //null returned if there is no more nodes
else
return (this->left)->find_node(v); //Call the find_node function recursively on the left node
}
else if(this->value < v) //if v is bigger than the node's value, search the next right node
{
if(this->right == nullptr) //checking if the next node on the left exists
return nullptr; //null returned if there is no more nodes
else
return (this->right)->find_node(v);// Call the find_node function recursively on the right node
}
return nullptr;// If the value is not found
}
'''

Your code needs lots of activation records on the call stack for repetitive calls to find_node(v). And it may lead to overflow of the call stack.
To avoid it, you can use non-recursive versions of binary search that uses a loop instead. For more information, check this link.

Related

Recursive function to return last node of a heap

I am trying to return the last node of a binary heap (implemented with pointers, not an array). Here, 'last' means the bottom-most node starting from the left in this case without two children, actually the node where I am supposed to append the new node to. The insert function will bind data to a new node for insertion, then call this function to return the last node, so I can add the new node either left of right depending on the child nodes present, or not.
The problem is that the right side of the tree is always empty, and never gets past the height after root's. Seems to stick on the leftmost side, because it reaches first the exit condition from every recursion call starting from left.
The recursive function checks first the node, returns 0 if no child, returns 1 if only left child and returns 2 in case of a node having two children. Here is the function :
template<typename T>
node<T> * heap<T>::find_insert_pos (node<T> *x, int &res) {
if(find_insert_poshelper(x, res) == 0) return x;
else if(find_insert_poshelper(x, res) == 1) return x;
else {
node<T> *a = find_insert_pos(x->left, res);
if(find_insert_poshelper(a, res) != 2) return a;
else return find_insert_pos(a, res);
node<T> *b = find_insert_pos(x->right, res);
if(find_insert_poshelper(b, res) != 2) return b;
else return find_insert_pos(b, res);
}
}
I've tried to figure it out, but insertion still goes wrong. The other functions used into insertion are more than triple checked.
(By the way, 'res' is passed by reference always in the chunk of code)
I have changed the logic behind the function. Instead of only validating for children per node, I validate now if the node evaluated had children, if it does then I validate one step further each of those children, left and right, to see if any of those grand-children have children themselves.
If they don't, I will loop this for the next level following the root level 0, jumping to level 1 and so on, until one of the children nodes does not contain two children, and returning x.left or x.right, depending the case.
-- Final edit --
Hard to think about a MRE since it was more about logic. The question was posted by someone in need of practice with recursion, and it happened. All the logic changed, even for sub-functions.
But it will be required to manually assign and narrow down until three levels are full (full meaning having two children) before calling this operation, which is checking three levels down. Having this done nicely I get a beautiful heap.
I can show an attempt to a MRE of how I implemented it to be able to find the bottom node to append a new node to, but not pure since I don't put the code from the 'insert' function, which is part iterative (first three levels) and part recursive (that was the original question, to find the parent node for the new node to insert). How the insert operation goes, I create a new node dynamically and then I search for the parent node where I need to append new data to (the iterative part starts here until the 8th node of the tree is reached, path similar to BFS), then when the position is retrieved (that is, the pointer itself), I test whether for left or right insertion, as by the rules of the heap. Starting at the 8th node, the value of the parent node is set recursively as follows :
First the recursive function itself :
node * function_a (node *x, int &res) {
node *temp = function_b (x, res);
if(temp != ptr_null) return temp;
else {
if(x->L != ptr_null) function_a (x->L, res);
if(x->R != ptr_null) function_a (x->R, res);
return temp;
}
}
A sub-function :
node * function_b (node *x, int &res) {
node *a = x->L;
node *b = x->R;
int aL = function_c (a->L, res);
int aR = function_c (a->R, res);
int bL = function_c (b->L, res);
int bL = function_c (b->R, res);
if(aL != 2) return a->L;
else if(aR != 2) return a->R;
else if(bL != 2) return b->L;
else if(bR != 2) return b->R;
else return ptr_null;
}
And another :
int & function_c (node *x, int &res) {
if(x->L == ptr_null && x.R == ptr_null) return res = 0;
else if(x->L != ptr_null && x->R == ptr_null) return res = 1;
else return res = 2;
}
Since this is checking 3 levels down from x defined (in this case from the root) in function_a, I can't make it 100% recursive that way or I will get a segmentation fault. How can I improve my algorithm ?

Segmentation fault (core dumped) - Threaded Binary Search Tree

I keep getting the following error : Segmentation fault (core dumped) . I found out the line of code that is causing the problem ( marked with a comment inside of the program) . Please tell me why this error is happening and how to fix it.
I've tried to dry run my code (on paper ) and see no logical errors (from my understanding).
I have only recently got into coding and stackoverflow please guide me through how I can further improve my question , as well as my code . Thanks !
class tree
{
struct node // Creates a node for a tree
{
int data;
bool rbit,lbit; // rbit/lbit= defines if right/left child of root is present or not
node *left,*right;
};
public:
node *head,*root;
tree() // constructor initializes root and head
{
root=NULL;
head=createnode(10000);
}
node *createnode(int value)
{// Allocates memory for a node then initializes node with given value and returns that node
node *temp=new node ;
temp->data=value;
temp->lbit=0;
temp->rbit=0;
temp->left=NULL;
temp->right=NULL;
return temp;
}
void insert(node *temp,int value) // Creates binary search tree node by node
{
if(root==NULL) // Checking if tree is empty
{
root=createnode(value); //Root now points to new memory location
head->left=root;
head->lbit=1;
root->left=head;//this line gives the segmentation fault (what i thought before correction)
}
}
void inorder(node *root) // Inorder traversal of tree (this function is logically incorrect)
{
if(root==NULL)
return;
inorder(root->left);
cout<<root->data<<"\t";
inorder(root->right);
}
void getdata()//Accepts data , creates a node through insert() , displays result through inorder()
{
int data;
cout<<"Enter data"<<endl;
cin>>data;
insert(root,data);
inorder(root);
}
/*void inorder(node *root) // Working inorder code
{
if(root->lbit==1)
inorder(root->left);
cout<<root->data<<"\t";
if(root->rbit==1)
inorder(root->right);
}*/
};
int main()
{
tree t; // Tree Object
t.getdata(); // Calling getdata
return 0;
}
I think the comments section largely reflects a miscommunication. It's easy to believe that you are experiencing a crash ON that particular line.
This is not actually the case. Instead what you have done is created a loop in your tree which leads to infinite recursion by the inorder function. That causes a stack overflow which segfaults -- this would have been extremely easy to spot if you had just run your program with a debugger (such as gdb) attached.
temp = createnode(value);
if(root == NULL)
{
root = temp;
head->left = root;
head->lbit = 1;
temp->left = head;
}
Look at the loop you have just created:
head->left points to root
root->left == temp->left, which points to head
An inorder traversal will now visit:
root
head
root
head
root
head
...
Since it never gets to the end of the left-branch, the function never outputs anything before overflowing the stack and crashing.
So no, your code is not logically correct. There's a fundamental design flaw in it. You need to rethink what you are storing in your tree and why.
From the code,
root=temp; //Root now points to temp
head->left=root;
head->lbit=1;
temp->left=head;// this line gives the segmentation fault
root is not pointing to temp. temp(pointer) is assigned to root(pointer).
head's left pointer is root, and temp's left is head (which means root's left is head). so in the function "inorder",
void inorder(node *root) // Inorder traversal of tree
{
if(root==NULL) <<<<<<
return;
inorder(root->left);
cout<<root->data<<"\t";
inorder(root->right);
}
the argument node *root (left) is never NULL and the function never return.
There's not enough information on exactly how this should work (what is node.lbit for example).
The question's insert() function will not work. It's passing in a value which is immediately overwritten (among other issues). There's no explanation of what tree.head is for, so it's ignored. The fields node.lbit and node.rbit look to be superfluous flags of node.left != NULL (similarly for right). These are omitted too. The insert() is also not creating the tree properly.
void insert(int value) // Insert a value into the tree (at branch)
{
// Create a new node to insert
struct node *temp = createnode(value);
if (root == NULL) // Checking if tree is empty
{
root = temp; //Root now points to temp
}
else
{
insertAtBranch(root, temp);
}
}
// recursively find the leaf-node at which to insert the new node
void insertAtBranch(node *branch, node *new_node)
{
// to create a BST, less-than go left
if (new_node->value <= branch->value)
{
if (branch->left == NULL)
branch->left = new_node; // There's no left-branch, so it's the node
else
insertAtBranch(branch->left, new_node); // go deeper to find insertion point
}
else // greater-than go right
{
if (branch->right == NULL)
branch->right = new_node;
else
insertAtBranch(branch->right, new_node);
}
}
Imagine how a binary tree works. New nodes are only ever inserted at the edges. So you look at a given node, and decide if this new-node is less or grater than the one you're looking at (unless the tree is empty, of course).
Say the new-node.value is less than the branch-node.value, you want to branch left. Still with the same node, if it doesn't have a left-branch (node.left == NULL), the new node is the left branch. Otherwise you need to travel down the left-branch and check again.
I would have made node a class, and used a constructor to at least set the default properties and value. But that's not a big deal.

Pointer to a new node in a Binary Tree

I was solving problem of insertion of node in a binary tree. I have the following doubts:
1) If we are inserting a node then we should return a pointer pointing to that node as then only we will be able to access the node, right?
2) Then here why are we returning root? We must return root->left or root->right accordingly, where am I wrong?
struct node* insert(struct node* root, int data)
{
if (root == NULL) //If the tree is empty, return a new,single node
return newNode(data);
else
{
//Otherwise, recur down the tree
if (data <= root->data)
root->left = insert(root->left, data);
else
root->right = insert(root->right, data);
return root;
}
}
3) Is this root which the above code returns the changed one from what it was previously due to recursion?
You misunderstand the return value.
The return value of this insert function is a pointer to the subtree that now has data inserted into it. If the passed in root was null, this is a new 1 node tree; if the passed in root is non-null, the return value is the same root.
This makes the recursion a bit simpler. We simply recurse until we run head-on into nullptr in a branch. Then the recursion stops, and the return value sets the parent's left or right node.
To create a brand new tree you type:
node* new_tree = insert(nullptr, 7);
to insert something into an existing tree you type:
existing_tree = insert(existing_tree, 7);
or equivalently
insert(existing_tree, 7);
so long as existing_tree isn't null.
This "double use" of the function (to both create and modify a tree) can confuse, but it makes the specific recursive use a tad less verbose, and makes the "empty tree is a nullptr" and "always do existing_tree = insert(existing_tree, val);" is a rule that makes the empty tree as the null tree work.
This is, however, a very C way of doing things.
A more c++ way of doing things would be:
std::unique_ptr<node> insert(std::unique_ptr<node> root, int data)
{
if (root == nullptr) //If the tree is empty, return a new,single node
return std::make_unique<node>(data);
else
{
//Otherwise, recur down the tree
if (data <= root->data)
root->left = insert(std::move(root->left), data);
else
root->right = insert(std::move(root->right), data);
return std::move(root);
}
}
where the flow of data into and out of the function is more explicit, and we assume node has a constructor that takes data.
This recursive insert should always return the very root node of the tree. Just because you read return root doesn't mean the original function call has finished executing, it just means the n'th recursion has finished. The recursive calls have all been pushed onto the stack and therefore must all be resolved before the original caller receives the returned value.
You can get back to the inserted node by doing a find for the inserted value.

Binary search tree node removal crashing program?

I need to write the code for my own BST. All the functions work accept I don't understand why I'm having trouble deleting a node.
Here is my first version of inserting a new node:
void insert(const T& arg)
{
if (!root)//tree is empty, as to not dereference prev at end
{
root = new PbstNode<T>(arg);//constructor sets both left and right to null
return;
}
PbstNode<T>* prev;
PbstNode<T>* traversal=root;
while (traversal)
{
prev=traversal;//prev is parent only after loop ends
if (arg < traversal->data)//new data is less than, send left
traversal = traversal->left;
else//new data is greater than or equal to, send right
traversal = traversal->right;
}
if (arg < prev->data)
prev->left = new PbstNode<T>(arg);
else
prev->right = new PbstNode<T>(arg);
}//end insert
Then I was thinking about how I could do it without keeping track of the predecessor node pointer, because I think if I tried to remove a node by first searching for it like above, I'd have to know if the ptr to the node to be deleted is a left or right node, and it may also be the root.
So then I tried writing an insert function where I could get the 'real' ptr to the insertion node position, without having to use prev->left or prev->right.
void insert(const T& arg)
{
PbstNode<T>** insertPtrPtr= &root;//
PbstNode<T>* aPtr;
while (aPtr= *insertPtrPtr)
{
if (arg < aPtr->data)//new data is less than, send left
insertPtrPtr = & aPtr->left;
else//new data is greater than or equal to, send right
insertPtrPtr = & aPtr->right;//insertPtrPtr = & (*insertPtrPtr)->right;
}
//i cant use aPtr here
*insertPtrPtr = new PbstNode<T>(arg);//constructor sets both left and right to nullptr
}//end insert
The above works after testing inserting various values and searching for them.
However, then I tried writing the remove function in a similar way:
void remove(const T& arg)//
{
PbstNode<T>** delPtrPtr= &root;
PbstNode<T>* aPtr;
while (aPtr= *delPtrPtr)
{
if (arg == aPtr->data)
{
//fixTree
delete (*delPtrPtr);//makes program crash?? had to be created with new
return;
}
else if (arg < aPtr->data)
{
delPtrPtr = & aPtr->left;
}
else
{
delPtrPtr = & aPtr->right;//delPtrPtr = & (*delPtrPtr)->right;
}
}
std::cout<<"Requested removal not found in tree.\n";
}//end remove
The line delete *delPtrPtr crashes the program, but the variable pointed by *delPtrPtr had to be created with new, right? What's going on?
Any reply is appreciated.

Removing from a Binary Search Tree

I am trying to write a remove from a binary tree function. I'm kinda lost so I'm trying to handle it case by case, starting with if the value I'm trying to remove is in the root of the BST. To test my function, I am first calling a printcontents() function that prints all the contents of the tree, then I'm calling remove(8) [8 being the value in my root at the moment), and then calling printcontents() again. The way I'm doing it is by trying to replace the root with the "right-most" value in the left side of the tree. When I call printcontents the second time, it prints the new root value correctly, but when it continues printing the contents and reaches the point where that value used to be, it has a random long number "-572......"(although i don't think the number matters) and then my program crashes. I see my root's value is being replaced, but what happens afterwards??
Here's my remove function:
void BinarySearchTree::remove(int value) {
Node* tmp = head;
Node* tmp2 = head;
if (head->data == value && head->left != NULL) {
tmp=tmp->left;
while (tmp->right != NULL) {
tmp=tmp->right;
}
while (tmp2->right->right != NULL) {
tmp2=tmp2->right;
}
if (tmp->left == NULL) {
head->data = tmp->data;
tmp2->right = NULL;
delete tmp;
}
if (tmp->left != NULL) {
head->data = tmp->data;
tmp2->right = tmp->left;
delete tmp;
}
}
It's obviously incomplete, but I'm testing it to only handle the case in which the root is removed and replaced by the right-most value in the left side of the tree (assuming there is a left side, which there is), and I feel like logically it should be working, so perhaps it is when I "delete tmp" that things go wrong. I don't know whether posting my whole program will be necessary, but if so, let me know!
May I suggest that instead of writing out for root, why don't you treat the case as it is dealt with in CLRS : That is two distinct cases.
1. When node to be deleted is a leaf
2. When node to be deleted is non-leaf(in that case replace it with inorder successor/predecessor).
The root deletion obviously falls under the second case. This is just a suggestion.