Binary Tree (not search) max function - c++

I'm getting a segmentation fault with my code and I'm not sure why. I'm trying to find the max value in a regular binary tree that is not ordered.
tnode<int> *maxT(tnode<int> *t)
{
if (t == NULL) return NULL;
tnode<int> *left = maxT(t->left);
tnode<int> *right = maxT(t->right);
if (left->nodeValue > right->nodeValue)
{
return maxT(left);
}
if (left->nodeValue < right->nodeValue)
{
return maxT(right);
}
}

The fundamentals of the algorithm are fairly straight forward. Because the tree is unordered, all nodes must be visited, with the following preconditions:
A null node pointer results in null as an answer.
Else a node with no children results in the current node
Else result is the max of the node compared to the max of its children.
Given that, I'm pretty sure this is what you're trying to do:
template<typename T>
tnode<T>* maxT(const tnode<T>* t)
{
if (!t)
return nullptr;
tnode<T>* lmax = maxT(t->left);
tnode<T>* rmax = maxT(t->right);
tnode<T>* cmax = (lmax && rmax)
? ((rmax->nodeValue < lmax->nodeValue ? lmax : rmax))
: (lmax ? lmax : rmax);
return (!cmax || (cmax->nodeValue < t->nodeValue) ? t : cmax);
}

tnode<int> *maxT(tnode<int> *t)
{
if (t->right == NULL && t->left == NULL) //leaf node
return t->nodeValue;
else if (t->right == NULL) //no right subtree
return MAX(t->nodeValue, maxT(t->left))
else if (t->left == NULL) //no left subtree
return MAX(t->nodeValue, maxT(t->right))
else
return MAX(maxT(t->right), maxT(t->left));
}
In your case, what happens if a node doesn't have a right or left child. Then either node->right == NULL or node->left == NULL. Yet you are trying to access left->nodeValue or right->nodeValue.

Related

How to fix segmentation fault in AVL deletion operation when rebalancing?

I am implementing an AVL tree and my search and insertion functions work properly, but I get a segmentation fault with my remove function. I have implemented a BST tree correctly before, so I know the issue is with the rebalancing of the tree rather than the initial deletion of a node.
Since my insertion operation works with the rebalancing, I also know the issue is not with the rotation functions themselves.
I have tried different strategies such as maintaining a balance factor at each node and have tried implementing other source code I have found online but I always end up with a segmentation fault and really cannot find where. I'd appreciate any help.
class AVL
{
public:
AVL();
Node* insert(int num);
Node* search(int num);
Node* remove(int num);
void print();
void comparisonPrint();
private:
int comparisonCount;
Node* root;
int max(int a, int b);
int getHeight(Node* t);
int getBalance(Node* t);
Node* insert(Node* &t, int num);
Node* rotateWithLeftChild(Node* t);
Node* rotateWithRightChild(Node* t);
Node* doubleRotateWithLeftChild(Node* t);
Node* doubleRotateWithRightChild(Node* t);
Node* search(Node* t, int num);
Node* removeMin(Node* parent, Node* node);
Node* remove(Node* &t, int num);
void print(Node* t);
//print
};
int AVL::max(int a, int b)
{
return (a > b)? a : b;
}
int AVL::getHeight(Node* t)
{
return (t == NULL) ? 0 : t->height;
}
int AVL::getBalance(Node* t)
{
if(t == NULL)
return 0;
return getHeight(t->leftChild) - getHeight(t->rightChild);
}
//helper function for remove - finds min
Node* AVL::removeMin(Node* parent, Node* node) //removes node, but does not delete - returns ptr instead
{
if(node != NULL)
{
if(node->leftChild != NULL) //go to leftmost child in right subtree
return removeMin(node, node->leftChild);
else //min val
{
parent->leftChild = node->rightChild;
return node;
}
}
else //subtree empty - incorrect use of function
return NULL;
}
Node* AVL::remove(Node* &t, int num)
{
cout << num;
if(t != NULL)
{
if(num > t->key)
{
comparisonCount++;
remove(t->rightChild, num);
}
else if(num < t->key)
{
comparisonCount++;
remove(t->leftChild, num);
}
else if(t->leftChild != NULL && t->rightChild != NULL)
{
comparisonCount++;
//2 children
Node* minRightSubtree = removeMin(t, t->rightChild);
t->key = minRightSubtree->key;
delete minRightSubtree;
}
else
{
comparisonCount++;
//0 or 1 child
Node* temp = t;
if(t->leftChild != NULL)
t = t->leftChild;
else if(t->rightChild != NULL)
t = t->rightChild;
delete temp;
}
//update height
t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;
int balance = getBalance(t);
if(balance > 1 && getBalance(t->leftChild) >= 0)
return rotateWithRightChild(t);
if(balance > 1 && getBalance(t->leftChild) < 0)
{
t->leftChild = rotateWithLeftChild(t->leftChild);
return rotateWithRightChild(t);
}
if(balance < -1 && getBalance(t->rightChild) <= 0)
return rotateWithLeftChild(t);
if(balance < -1 && getBalance(t->rightChild) > 0)
{
t->rightChild = rotateWithRightChild(t->rightChild);
return rotateWithLeftChild(t);
}
}
return t;
}
So I need the remove function to remove a specified node and rebalance the tree when necessary using the appropriate rotations. However, I keep getting a segmentation fault whenever I try to call the function in my main. Thanks.
There are two problems with your code. First is the removeMin function and the else if part in remove function when the node to be deleted has two children.
Basic aim of the removeMin function should be to find the inorder successor of the node to be deleted which is t in your case. Consider the case when t has 2 children (both leaf nodes) then your removeMin function will set t->leftChild as t->rightChild->rightChild which is NULL which is wrong. Also the restructuring of the tree should be done inside remove hence removeMin becomes:
Node* AVL::removeMin(Node* node) // returns inorder successor of 't'
{
if(node->left == NULL)
return node;
return removeMin(node->left);
}
Coming to remove function, we reset t->key with minRightSubtree->key and the node to be deleted now is minRightSubtree. But notice that the order of keys has changed in the chain from node t till node minRightSubtree. t->key is less than all the keys of nodes till before minRightSubtree. Hence you cannot just delete minRightSubtree, you have to call remove function on the node minRightSubtree which will take care of restructuring this chain. Also you can get a little help from the recursion stack to get the correct child for the current node t after deletion/rotation:
Node* AVL::remove(Node* &t, int num)
{
if (t == NULL)
return NULL;
if (num > t->key)
t->rightChild = remove(t->rightChild, num);
else if (num < t->key)
t->leftChild = remove(t->leftChild, num);
else if (t->leftChild != NULL && t->rightChild != NULL)
{
//2 children
Node* minRightSubtree = removeMin(t->rightChild);
t->key = minRightSubtree->key;
t->rightChild = remove(t->rightChild, minRightSubtree->key);
}
else
{
//0 or 1 child
Node* temp = t;
if (t->leftChild != NULL)
t = t->leftChild;
else if (t->rightChild != NULL)
t = t->rightChild;
if(temp == t)
t = NULL;
delete temp;
}
if (t == NULL) // this case was added since there is a possibility of deleting 't'
return NULL;
//update height
t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;
int balance = getBalance(t);
if (balance > 1 && getBalance(t->leftChild) >= 0)
return rotateWithRightChild(t);
if (balance > 1 && getBalance(t->leftChild) < 0)
{
t->leftChild = rotateWithLeftChild(t->leftChild);
return rotateWithRightChild(t);
}
if (balance < -1 && getBalance(t->rightChild) <= 0)
return rotateWithLeftChild(t);
if (balance < -1 && getBalance(t->rightChild) > 0)
{
t->rightChild = rotateWithRightChild(t->rightChild);
return rotateWithLeftChild(t);
}
return t;
}
I'm assuming your code for updating heights and balancing the rooted sub-tree is correct since I've forgotten about it's theory and will need to revise.

Is given binary tree is binary search tree or not

I have written a function that returns true if given binary tree is binary search tree else returns false.
bool IsBst(node* root)
{
if(root->left == NULL && root->right == NULL)
{
return true;
}
if(root->left->data <= root->data && root->right->data > root->data)
{
return (IsBst(root->left) && IsBst(root->right))
}
else
{
else false;
}
}
Is my function right?
Will this function return right answer?
I have doubt in case of if left child is null then what will this comparison root->left->data<=root->data return?(If there is NULL)
Help me to improve this!
Thanks in advance!
It should be something like
bool IsBst(const node* root, node* minNode = nullptr, node* maxNode = nullptr)
{
if (root == nullptr) {
return true;
}
if (minNode != nullptr && root->data < minNode->data)
{
return false;
}
if (maxNode != nullptr && maxNode->data < root->data)
{
return false;
}
return IsBst(root->left, minNode, root)
&& IsBst(root->right, root, maxNode);
}
If you're using C++ 17 and above, you can do it even more elegantly by using an optional class. Hence, you don't need to do nullptr checks for min and max:
bool checkBST0(const Node* root, const std::optional<int>& min, const std::optional<int>& max) {
if (root != nullptr) {
const auto data = root->data;
if ((min.has_value() && min >= data) ||
(max.has_value() && max <= data)) {
return false;
}
std::optional<int> opt(data);
return checkBST0(root->left, min, opt) && checkBST0(root->right, opt, max);
}
return true;
}
Initially, you should call this method with an optional without any value:
std::optional<int> emptyOptional;
return checkBST0(root, emptyOptional, emptyOptional);
Nope, it's not right. It would fail on this tree:
3
\
\
5
And it would give a wrong answer on this one:
4
/ \
/ \
/ \
2 6
/ \ / \
1 9 0 8
A BST is defined as a tree, whose each internal node stores a key greater than all the keys in the node’s left subtree and less than those in its right subtree (see the Wikipedia article).
So it's not enough for a 1-2-9 left subtree in my example to have a left node value less than it's root (1<2) and the right node greater than it (9>2). It should also satisfy the condition that all its nodes have values less than 4, the value in the whole tree's root.
Here is an example in C I gave in the answer to the question Pseudo code to check if binary tree is a binary search tree - not sure about the recursion:
// Test a node against its closest left-side and right-side ancestors
boolean isNodeBST(NODE *lt, NODE *node, NODE *rt)
{
if(node == NULL)
return true;
if(lt != NULL && node->key <= lt->key)
return false;
if(rt != NULL && node->key >= rt->key)
return false;
return
isNodeBST(lt, node->left, node) &&
isNodeBST(node, node->right, rt);
}
boolean isTreeBST(TREE *tree)
{
return isNodeBST( NULL, tree->root, NULL);
}

Binary search tree deletion (C++)

I'm having trouble implementing a binary search tree deletion algorithm on C++. If I try deleting the root, or direct children of the root, it works correctly. But it does not work for deeper levels (just outputs the same tree without any deletion). What is wrong with my code?
typedef struct Node {
int key;
Node *left = NULL;
Node *right = NULL;
} Node;
...
/*
* Delete <key> from BST rooted at <node> and return modified <node>.
*/
Node* BST::pop(Node *node, int key) {
// If <node> is a null pointer, return.
// If <node> doesn't contain the key, traverse down the tree.
// If <node> contains the key, perform deletion.
if (node == NULL) {
} else if (key < node->key) {
node->left = pop(node->left, key);
} else if (key > root->key) {
node->right = pop(node->right, key);
} else {
// If <node> is a leaf, just delete it
if (node->left == NULL && node->right == NULL) {
delete node; // deallocate memory (note: node still points to a memory address!)
node = NULL; // node points to null
}
// If <node> has a single child, replace <node> with its child
else if (node->left == NULL && node->right != NULL) {
Node *tmp = node;
node = node->right;
delete tmp;
tmp = NULL;
} else if (node->right == NULL && node->left != NULL) {
Node *tmp = node;
node = node->left;
delete tmp;
tmp = NULL;
} else {
node->key = findMax(node->left);
node->left = pop(node->left, node->key);
}
}
return node;
}
int BST::findMax(Node *root) {
if (root->left == NULL && root->right == NULL) {
return root->key;
} else {
int max = root->key;
if (root->left != NULL) {
int leftMax = findMax(root->left);
if (leftMax > max) {
max = leftMax;
}
}
if (root->right != NULL) {
int rightMax = findMax(root->right);
if (rightMax > max) {
max = rightMax;
}
}
return max;
}
}
A couple of things:
Second else if should be else if (key > node->key)
Your findMax function is exceptionally complex. The max in a BST from some root is really just traversing right children until there are no more right children (because anything in the left subtree must be less than the key you are currently evaluating, so leftMax can never be > max). Therefore it could be
int BST::findMax(Node *root) {
int max = root->key;
while (root->right != NULL) {
root = root->right;
max = root->key;
}
return max;
}
As long as the tree does not need to remain balanced, your general algorithm of just removing in case of a leaf, swapping the lone child if there is only one, and in the case of two children finding an inorder neighbor, swapping and deleting that node should be sound (not sure if you found this link, but: http://quiz.geeksforgeeks.org/binary-search-tree-set-2-delete/)

Implementation of removeNode for a Binary Search Tree

I'm trying to implement a removal algorithm discussed in a textbook for a binary search tree in a program, but the book is scant on details for some of the functions described so I've guessed at their meaning and implemented the functions it specified and some of my own. The problem I'm having is with the removeNode function on handling the 0-1-2 children cases.
In the book it specifies the following pseudocode for removeNode
removeNode(N: BinaryNode)
{
if(N is a leaf)
Remove N from the tree
else if (N has only one child C)
{
if(N was a left child of its parent P)
Make C the left child of P
else
Make C the right child of P
}
else //Node has two children
{
//Find S, the node that contains N's inorder successor
//Copy the item from node S into node N
//Remove S from the tree by using the previous
//technique for a leaf or a node with one child
}
In this function, how do you make C a child of P? given a single node with nothing to point back to the parent what can you do to figure out who the parent of the tree is? Usually you need a trailing node to keep track of that but due to the books 'final draft' I suspect that wasn't what they were implying.
'Final Draft'
removeNode(nodePtr: BinaryNodePointer): BinaryNodePointer
{
if(N is a leaf)
{
//Remove leaf from the tree
delete nodePtr
nodePtr = nullPtr
return nodePtr
}
else if (N has only one child C)
{
if(N was a left child of its parent P)
nodeToConnectPtr = nodePtr->getleftChildPtr() //<---I assume this means nodePtr->left
else
nodeToConnectPtr = nodePtr->getRightChildPtr() //<--nodePtr->right?
delete nodePtr
nodePtr = nullptr
return nodeToConnectPtr
}
else //Node has two children
{
//Find the inorder succesor of the entry in N: it is in the left subtree rooted
//at N's Child
tempPtr = removeLeftMosstNode(nodePtr->getRightChild(), newNodeValue)
nodePtr->setRightChildPtr(tempPtr) //<--nodePtr->right = tempPtr?
nodePtr->setItem(newNodeValue) // nodePtr->vendorData = newNodeValue?
return nodePtr
}
This is the implementation I came up with based off the aforementioned design. I know some parts are wrong but I wasn't sure what else I could do to fix them. Could anyone suggest a fix the child cases and any other problems I might have missed?
My Implementation
aBst::treeNode * aBst::removeNode(aBst::treeNode * nodePtr)
{
//This functions deletes a node and then returns the pointer to the child to take the place of deleted child
aVendor * tempVendorPtr;
treeNode * nodeToConnectPtr, *tempPtr;
//The node passed is the node that needs to be removed
if (nodePtr->right == NULL && nodePtr->left == NULL) //----No Child----
{
delete nodePtr;
nodePtr = NULL;
return nodePtr;
}
else if ((nodePtr->right != NULL) != (nodePtr->left != NULL))//----One Child----
{
if (nodePtr->left != NULL)//left child
{
nodeToConnectPtr = nodePtr->left; //Wrong
}
else if (nodePtr->right != NULL) //right child
{
nodeToConnectPtr = nodePtr->right; //Wrong
}
delete nodePtr;
nodePtr = NULL;
return nodeToConnectPtr;
}
else //-----Two Child-----
{
//find minimum value of right subtree, stores the pointer to the vendorData it carries through the parameter and calls removeNode
tempPtr = removeLeftMostNode(nodePtr->right, tempVendorPtr);
nodePtr->vendorData = tempVendorPtr;
nodePtr->right = tempPtr;
return nodePtr;
}
}
All functions
int aBst::countKids(aBst::treeNode * subTreePtr)
{
if (subTreePtr == NULL) //Empty Tree
{
return -1;
}
else if (subTreePtr->right == NULL && subTreePtr->left == NULL) //----No Child----
{
return 0;
}
else if ((subTreePtr->right != NULL) != (subTreePtr->left != NULL))//----One Child----
{
return 1;
}
else if ((subTreePtr->right != NULL) && (subTreePtr->left != NULL))//----Two Child----
{
return 2;
}
//Something unexpected occurred
return -1;
}
bool aBst::remove(char nameOfVendor[])
{
bool failControl = false;
removeValue(root, nameOfVendor, failControl);
return failControl;
}
aBst::treeNode * aBst::removeValue(aBst::treeNode * subTreePtr, char nameOfVendor[], bool& success)
{
//Note: the subTreePtr should be root in initial call
treeNode * tmpPtr;
char name[MAX_CHAR_LENGTH];
//Make sure passed success bit is false
success = false;
subTreePtr->vendorData->getName(name);
if (subTreePtr == NULL) //Empty Tree
{
success = false;
return NULL;
}
else if (strcmp(name, nameOfVendor) == 0) //Evaluates to true if there is a match
{
subTreePtr = removeNode(subTreePtr);
success = true;
return subTreePtr;
}
else if (strcmp(name, nameOfVendor) > 0) // Go left
{
//Protects algorithm from bad data crash
if (subTreePtr->left == NULL)
{
return subTreePtr;
}
tmpPtr = removeValue(subTreePtr->left, nameOfVendor, success);
subTreePtr->left = tmpPtr;
return subTreePtr;
}
else // Go Right
{
//Protects algorithm from bad data crash
if (subTreePtr->right == NULL)
{
return subTreePtr;
}
tmpPtr = removeValue(subTreePtr->right, nameOfVendor, success);
subTreePtr->right = tmpPtr;
return subTreePtr;
}
//For loop was broken and function returns false
return subTreePtr;
}
aBst::treeNode * aBst::removeNode(aBst::treeNode * nodePtr)
{
aVendor * tempVendorPtr;
treeNode * nodeToConnectPtr, *tempPtr;
//The node passed is the node that needs to be removed
if (nodePtr->right == NULL && nodePtr->left == NULL) //----No Child----
{
delete nodePtr;
nodePtr = NULL;
return nodePtr;
}
else if ((nodePtr->right != NULL) != (nodePtr->left != NULL))//----One Child----
{
if (nodePtr->left != NULL)//left child
{
nodeToConnectPtr = nodePtr->left;
}
else if (nodePtr->right != NULL) //right child
{
nodeToConnectPtr = nodePtr->right;
}
delete nodePtr;
cout << "called\n";
nodePtr = NULL;
return nodeToConnectPtr;
}
else //-----Two Child-----
{
//find minimum value of right subtree, stores the pointer to the vendorData it carries through the parameter and calls removeNode
tempPtr = removeLeftMostNode(nodePtr->right, tempVendorPtr);
nodePtr->vendorData = tempVendorPtr;
nodePtr->right = tempPtr;
cout << "\nleaving Two Child\n";
return nodePtr;
}
}
aBst::treeNode * aBst::removeLeftMostNode(aBst::treeNode * nodePtr, aVendor*& vendorDataRef)
{
if (nodePtr->left == NULL)
{
//Target acquired
vendorDataRef = nodePtr->vendorData;
return removeNode(nodePtr);
}
else
return removeLeftMostNode(nodePtr->left, vendorDataRef);
}
I think you have a similar problem as I do. What you're doing when there is only one child, is just setting the pointer to branch to the right or left respectively. But you need to replace the node with a node from that subtree. This can be done by searching for the minimum node in the left subtree and replacing the node you wanted to remove with this minimum node. Then you need to remove the node you just inserted to prevent node duplication. That's the theory anyway. I haven't managed to implement it correctly myself.
edit: I removed the link again. I saw that it is considered bad etiquette to ask something in an answer. Shame on me /o.

Why would a recursive function be called on a pointer after NULL check?

// So I call this function after deleting a node.
// It works before I delete the node, but for some reason
// after I perform a deletion and update the tree it runs into
// EXC_BAD_ACCESS on the line below...
void BinaryTree::updateCost(BinaryNode *root) {
if (root != NULL)
root->updateCostRecursively(1);
}
void BinaryNode::updateCostRecursively(int newCost) {
cout << this << endl; // prints 0x3000000000000000 before the bad access
cost = newCost; // has a bad access here
if (right != NULL)
right->updateCostRecursively(newCost + 1);
if (left != NULL)
left->updateCostRecursively(newCost + 1);
}
Why is this recursive function called on the NULL object even when I check the pointer each time?
I have copied the code I use to delete a node below. I'm still having trouble understanding recursive functions, but from what can tell at no point am I leaving a dangling pointer.
BinaryNode *BinaryTree::findMin(BinaryNode *t) {
if (t == NULL) return NULL;
while (t->left != NULL) t = t->left;
return t;
}
BinaryNode *BinaryTree::removeMin(BinaryNode *t) {
if (t == NULL) return NULL;
if (t->left != NULL)
t->left = removeMin(t->left);
else {
BinaryNode *node = t;
t = t->right;
delete node;
}
return t;
}
bool BinaryTree::remove(int key) {
if (root != NULL && remove(key, root))
return true;
return false;
}
BinaryNode *BinaryTree::remove(int x, BinaryNode *t) {
if (t == NULL) return NULL;
if (x < t->key)
t->left = remove(x, t->left);
else if (x > t->key)
t->right = remove(x, t->right);
else if (t->left != NULL && t->right != NULL) {
// item x is found; t has two children
t->key = findMin(t->right)->key;
t->right = removeMin(t->right);
} else { //t has only one child
BinaryNode *node = t;
t = (t->left != NULL) ? t->left : t->right;
delete node;
}
updateCost(root);
return t;
}
The error is in your delete method, not the code you posted. After you delete a node (say root->right) you need to set root->right = NULL. All you're doing with delete is freeing the memory that pointer points to. The pointer itself continues to point to that address. You're getting a bad access exception because you're trying to access the freed memory.