I am coding a binary search tree.
class Node {
public:
Node* left;
Node* right;
int data;
Node(int x) : data(x) {}
void insert(int value) {
if (value < data) {
if (left == NULL)
left = new Node(value);
else
left->insert(value);
} else {
if (right == NULL)
right = new Node(value);
else
right->insert(value);
}
}
bool contains(int value) {
if (value == data)
return true;
else if (value < data) {
if (left == NULL)
return false;
else
return left->contains(value);
} else {
if (right == NULL)
return false;
else
return right->contains(value);
}
}
};
When I use Node x in the main program and then call x.insert(15) it gives a segmentation error. If I use Node* x=new Node(10) and then use x->insert(15) instead then it works fine. What is the reasoning behind that?
int main() {
Node x(10);
x.insert(15);
}
The main problem is that you don't initialize the pointers (left and right) with nullptr but you assume they are initialized with a null pointer in insert. Doing the initialization in the constructor fixes the issue:
Node(int x) : data{x}, left{nullptr}, right{nullptr} {}
Related
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.
I was trying to implement AVL Tree in C++, I Implemented key insertion without much problem but while trying to delete a node I ran into use after free bug. I tried to debug with gdb but I was unable to find out the problem, Here's the full code.
#include <iostream>
#include <algorithm>
using namespace std;
class Node;
class AVLTree;
typedef Node* node_ptr;
class Node {
int key;
int height;
node_ptr left;
node_ptr right;
void fix_height() {
int hR, hL = 0;
if (left) hL = left->height;
if (right) hR = right->height;
height = max(hL, hR) + 1;
}
int balance_factor() {
if (left && right)
return left->height - right->height;
if (left && !right)
return left->height;
if (right && !left)
return -right->height;
return 0;
}
public:
Node(int key) :key(key), left(nullptr), right(nullptr),height(1){}
friend class AVLTree;
};
class AVLTree {
node_ptr mRoot;
node_ptr insert(node_ptr node, int key) {
if (!node) return new Node(key);
if (node->key > key) node->left = insert(node->left, key);
else node->right = insert(node->right, key);
return balance(node);
}
node_ptr left_rotate(node_ptr X) {
node_ptr Y = X->right;
X->right = Y->left;
Y->left = X;
X->fix_height();
Y->fix_height();
return Y;
}
node_ptr right_rotate(node_ptr Y) {
node_ptr X = Y->left;
Y->left = X->right;
X->right = Y;
X->fix_height();
Y->fix_height();
return X;
}
node_ptr balance(node_ptr node) {
node->fix_height();
int b_factor = node->balance_factor();
if (b_factor == 2) {
if (node->left->balance_factor() < 0) {
node->left = left_rotate(node->left);
}
return right_rotate(node);
}
else if (b_factor == -2) {
if (node->right->balance_factor() > 0) {
node->right = right_rotate(node->right);
}
return left_rotate(node);
}
return node;
}
node_ptr find_min(node_ptr node) {
if (!node) return nullptr;
if (!node->left) return node;
else return
find_min(node->left);
}
node_ptr remove_min(node_ptr node) {
if (!node->left)
return node->right;
node->left = remove_min(node->left);
return balance(node);
}
node_ptr remove(node_ptr node, int key) {
if (!node) return nullptr;
if (node->key > key)
node->left = remove(node->left, key);
else if (node->key < key)
node->right = remove(node->right, key);
else {
node_ptr L = node->left;
node_ptr R = node->right;
delete node;
if (!R) return L;
node_ptr min = find_min(node->right);
min->right = remove_min(R);
min->left = L;
return balance(min);
}
return balance(node);
}
void print_inorder(node_ptr node) {
if (node) {
print_inorder(node->left);
cout << node->key << " ";
print_inorder(node->right);
}
}
public:
AVLTree() :mRoot(nullptr) {}
void insert(int key) {
mRoot = insert(mRoot, key);
}
void remove(int key) {
mRoot = remove(mRoot, key);
}
void print_inorder() {
cout << endl;
print_inorder(mRoot);
cout << endl;
}
};
int main()
{
AVLTree mtree;
for (int i = 0; i < 3; ++i) {
mtree.insert(i);
}
mtree.remove(0);
mtree.remove(1);
mtree.remove(2);
mtree.print_inorder();
return 0;
}
So, In remove_min(), I am using the same logic as a binary search tree. If sub-tree has a right child, minimum element from that right subtree is returned, and replaced with the target node, If not then just pointer to left subtree is returned and as function returns it balances the disturbed nodes. But somehow deleted node is getting referenced and I am getting segmentation fault. I cannot figure out how. Can someone help ?
I was trying to use to insert into a binary tree using while loop. However it seems to have a bug: it only takes the first number and doesn't take the rest. I don't know where I made the mistake.
I've created the following code:
void BTC::Insert(int Data)
{Node *newNode = new Node;
if (head == 0)
{
head = node;
}
else
{
Node* ptr = head;
if(ptr->childs>2)
{
if (Data > ptr->data)
{
ptr->right = node;
}
else if (Data <= ptr->data)
{
ptr->left = node;
}
ptr->childs++;
}
else
{
While(ptr->childs==2)
{
if (Data > ptr->data)
{
ptr = ptr->right;
}
else if (Data <= ptr->data)
{
ptr = ptr->left;
}
}
}
}
class Node
{
public:
int data;
Node* right;
Node* left;
int childs;
Node() : right(0), left(0)
{}
Node(int data) : data(data), right(0), left(0),childs(0)
{}
}
}
This is a binary search tree implementation, I cant figure out why my min method (for finding the minimum element in a tree) is not returning the correct answer, but an arbitrary memory address.
I am creating a tree by this constructor BST(3);, now I run min(), it returns correctly 3, but after inserting 1(insert(1) method), min() returns some hex address.
class node{
public:
int key;
node *left;
node *right;
node *parent;
};
class BST{
node *root;
public:
BST(){}
BST(int a){
root=new node();
root->left=NULL;
root->right=NULL;
root->parent=NULL;
root->key=a;
}
void insert(int n)
{
if(search(n))return;
node *p=root;
node *m=new node;
m->key=n;
m->left=NULL;
m->right=NULL;
while(1)
{
if(p->key > n)
{
//look left
if(p->left==NULL)
{
p->left=m;
m->parent=p;
return;
}
else
p=p->left;
}
else
{
//look right
if(p->right==NULL)
{
p->right=m;
m->parent=p;
return;
}
else
p=p->right;
}
}
}
bool search(int n)
{
node *p=root;
while(1)
{
if(p->key > n)
{
//look left
if(p->left==NULL)
return false;
else
p=p->left;
}
else if(p->key==n)return true;
else
{
//look right
if(p->right==NULL)
return false;
else
p=p->right;
}
}
}
int min()
{
node *p=root;
if(p->left == NULL)
return (p->key);
p=p->left;
}
};
Because you run into undefined behaviour by not returning on all control paths:
int min()
{
node *p=root;
if(p->left == NULL)
return (p->key);
p=p->left;
//no return here
}
Which means that if p->left is not NULL, anything can happen. Anything!
It looks like you want a loop there instead:
int min()
{
node *p=root;
while (p->left != NULL)
p=p->left;
return (p->key);
}
If p->left != NULL, you don't return anything.
I'm getting a segmentation fault on my program when I try to insert to a binary search tree. Here's the declaration of the node:
template < class T > class binTreeNode {
friend class binTree < T >;
friend class binSTree < T >;
public:
// default constructor
binTreeNode ( const T& newData =T( ), binTreeNode < T >* newLeft = 0, binTreeNode < T >* newRight = 0 ) {
data = newData;
left = newLeft;
right = newRight;
}
private:
T data; // data value in node
binTreeNode < T > *left, *right; // links to other nodes
};
The functions below are all new, everything else (like height functions and constructors) are all done in the parent class, and shouldn't really be relevant. The new functions are:
template <class T>
class binSTree : public binTree<T> {
public:
void insert (const T& newData) {
if (root == NULL) {
root = new binTreeNode<T>(newData);
}
else
insert(root,newData);
}
bool search (const T& x) const {
if (root != NULL)
return search(root,x);
else
return false;
}
bool remove (const T& x) {
if (root == NULL)
return false;
remove(root,x);
return true;
}
protected:
binTreeNode<T>* root;
private:
void insert (binTreeNode<T>*& ptr, const T& x) {
if (ptr == NULL) { // Base case, actual insertion
binTreeNode<T>* newNode;
newNode = new binTreeNode<T>(x);
ptr = newNode;
return;
}
if (x == ptr->data)
return;
else if (x < ptr->data)
insert(ptr->left,x);
else
insert(ptr->right,x);
return;
}
bool search (binTreeNode<T>* ptr, const T& x) const {
if (ptr->data == x)
return true;
else if (x < ptr->data && ptr->left != NULL)
search(ptr->left,x);
else if (ptr->right != NULL)
search(ptr->right,x);
else
return false;
}
binTreeNode<T>* remove (binTreeNode<T>* ptr, const T& x) {
if (ptr == NULL)
return NULL;
else if (ptr->data == x && leaf(ptr)) {
delete ptr;
ptr = NULL;
return ptr;
}
else if (ptr->data == x && !leaf(ptr))
return ptr;
else if (x < ptr->data) {
ptr->left = remove(ptr->left,x);
return ptr;
}
else {
ptr->right = remove(ptr->right,x);
return ptr;
}
}
bool leaf (binTreeNode<T>* node) const {
if (node->left != NULL || node->right != NULL)
return false;
return true;
}
};
The segmentation fault, according to valgrind, is in the private insert in the conditional where I check if (x == ptr->data). Does anyone have any idea why this is? I've completely hit a wall. Thanks :3
There is a problem in your remove code that may or may not be the cause of your crash, but should definitely be fixed: when you recursively remove ptr->left or ptr->right that results in deleting the node, you should also set the left or right pointer in the parent to NULL; otherwise you open up your code to errors associated with dangling pointers.