I'm learning binary trees and want to implement with OOP where I have a struct Node and create a BST Object. I'm trying to create an insert function with this approach and am running into the issue where I can't recursively traverse the tree to add a new node - that is, unless I overload the method, essentially copying it, to call the new method with a pointer to left or right. Hard to explain, but right now I have two methods, and I'm not sure if I'm missing something obvious to just have 1 method with 1 parameter int data, or if this approach just isn't correct. I feel like there's something valuable for me to learn here. Many thanks.
#include <iostream>
struct Node
{
Node *right;
Node *left;
int data;
};
class BST
{
public:
Node* root;
public:
BST()
:root(NULL)
{
}
//inserts node taking parameter data
Node* insertNode(int data)
{
//if tree is empty, create root
if (root == NULL)
{
root = newNode(data);
}
//if data is smaller than or equal to root, insert left
else if (data <= root->data)
{
root->left = insertNode(root->left, data);
}
//data is larger than root, insert right
else
{
root->right = insertNode(root->right, data);
}
return root;
}
//inserts new node
Node* insertNode(Node *root, int data)
{
//if tree is empty, create root
if (root == NULL)
{
root = newNode(data);
}
//if data is smaller than or equal to root, insert left
else if (data <= root->data)
{
root->left = insertNode(root->left, data);
}
//data is larger than root, insert right
else
{
root->right = insertNode(root->right, data);
}
return root;
}
Node* newNode(int data)
{
Node *temp = new Node;
temp->data = data;
temp->left = NULL;
temp->right = NULL;
return temp;
}
};
int main() {
BST bst1;
bst1.insertNode(30);
bst1.insertNode(15);
return 0;
}
You can save the redundancy by having one call forward to the other:
Node* insertNode(int data)
{
return insertNode(root, data);
}
Note that having identical names for your class member (Node* root) and the local variable in Node* insertNode(Node *root, int data) is error-prone.
Also please do not forget to delete what you new.
Related
I have implemented binary search tree in C++ and for some reason I am not seeing where the segmentation fault occurs. But I do notice that when I comment out root = node in the first conditional statement in addNode the error goes away. What exactly is a segmentation fault and how does it related to pointers?
#include <iostream>
#include <iomanip>
using namespace std;
class bstNode
{
public:
int value;
bstNode *left;
bstNode *right;
bstNode(){};
~bstNode(){};
bstNode(int value)
{
this->value = value;
this->left = NULL;
this->right = NULL;
}
bstNode(int value, bstNode *left, bstNode *right)
{
this->value = value;
this->left = left;
this->right = right;
}
bstNode *root;
void addNode(int value)
{
if (root == NULL)
{
bstNode *node = new bstNode(value);
root = node;
}
else
{
bstNode *focusNode = root;
bstNode *parent;
while (focusNode != NULL)
{
if (value > focusNode->value)
{
focusNode = focusNode->right;
if (focusNode == NULL)
{
focusNode->right = new bstNode(value);
}
}
else
{
focusNode = focusNode->left;
if (focusNode == NULL)
{
focusNode->left = new bstNode(value);
}
}
}
}
}
static void printBST(bstNode *node)
{
while (node != NULL)
{
printBST(node->left);
cout << node->value;
printBST(node->right);
}
}
};
int main()
{
bstNode *node = new bstNode();
node->addNode(7);
node->addNode(2);
node->addNode(18);
node->addNode(6);
node->addNode(4);
node->addNode(23);
bstNode::printBST(node->root);
return 0;
}
The immediate error is this
if (focusNode == NULL) {
focusNode->left = new bstNode(value);
}
this is clearly wrong, if a pointer is null you cannot use it. You have this in multiple places. Fix that and then update the question once you have got past that. How did I know this? I ran your code under my debugger and it told me immediatley, you should learn how to get the most out of your debugger.
Next
void addNode(int value)
as a method for a class defined as
class bstNode {
public:
int value;
is very bad practice. In that method what does value refer to? The argument or the member variable. Get into the habit of giving member variables specific names like this
class bstNode {
public:
int value_;
Also minor nits. The accepted style for naming classes is with leading Caps like this
class BstNode {
public:
int value_;
or even
class BSTNode
class bstNode {
public:
int value_;
using namespace std;
I'd advise against doing this in general. It's hard to be sure what's in namespace std, but the short summary is "a lot, and more all the time", so making all of it visible directly can lead to problems.
bstNode(){};
~bstNode(){};
These don't really accomplish anything useful. The point of a constructor is to initialize the object, but these just leave the object uninitialized, which can lead to problems--especially segmentation faults when/if you try to dereference an uninitialized pointer.
bstNode(int value){
this->value = value;
this->left = NULL;
this->right = NULL;
}
This is better, but I'd prefer to use a member initializer list instead of assignments inside the body of the ctor, and I'd prefer nullptr over NULL:
bstNode(int value)
: value(value)
, left(nullptr)
, right(nullptr) {}
This next one:
bstNode(int value, bstNode* left, bstNode* right){
this->value = value;
this->left = left;
this->right = right;
}
...is pretty nicely written (though it could also use a member initializer list, which is usually preferable), but only rarely useful when building a binary search tree, because in normal use you only ever insert new leaf nodes, not new internal nodes.
void addNode(int value){
if (root == NULL){
bstNode* node = new bstNode(value);
root = node;
}
else{
bstNode* focusNode = root;
bstNode* parent;
while(focusNode != NULL){
if(value > focusNode->value){
focusNode = focusNode->right;
if(focusNode == NULL){
focusNode->right = new bstNode(value);
}
}
else{
focusNode = focusNode->left;
if(focusNode == NULL){
focusNode->left = new bstNode(value);
}
}
}
}
}
This is at least one obvious source of a segmentation fault--you dereference a pointer immediately after verifying that it's null.
At least for a first attempt, I think I'd use a recursive implementation, which tends to be simpler:
void addNode(int value, bstNode *&node = root) {
if (node == nullptr) {
node = new node(value);
} else if (value < node->value) {
addNode(value, node->left);
} else if (value > node->value) {
addNode(value, node->right);
} else {
// attempt at inserting duplicate value
}
}
Note that this passes a reference to a pointer, so we can modify the "current" pointer, rather than having to track the parent pointer while traversing the tree.
static void printBST(bstNode* node){
while(node != NULL){
printBST(node->left);
cout << node->value;
printBST(node->right);
}
}
Since we're doing this recursively, we don't need (or even want) a loop. Traversing the left sub-tree, the current node, and the right subtree traverses the entire tree, with no iteration needed.
Also note that this doesn't print any delimiter between the numbers in the nodes, so a tree containing 12, 34 and a tree containing 1, 2, 3, 4 will both be printed out as 1234, which probably isn't very useful. Fortunately, adding a delimiter is pretty easy.
static void printBST(bstNode* node){
if (node != nullptr){
printBST(node->left);
cout << node->value << ' ';
printBST(node->right);
}
}
In the the following code...
while(focusNode != NULL){
if(value > focusNode->value){
focusNode = focusNode->right;
if(focusNode == NULL){
focusNode->right = new bstNode(value);
}
}
else{
focusNode = focusNode->left;
if(focusNode == NULL){
focusNode->left = new bstNode(value);
}
}
}
...you are referencing the children of a node that is guaranteed to be NULL because you verified that using the conditional statement. Since the node itself does not exist, it doesn't have properties like children. Imagine you're trying to communicate with the child of a person who has never existed.
The variable focusNode stores an address of a node. What focusNode->value does is that it goes to the node whose address focusNode stores and retrieves the value property from there.
When focusNode is NULL, it doesn't point to any node, thus you can't go there and retrieve its value property.
I wrote the code that you can replace with your while loop. I have tested it and it works:
while(true){
if(value > focusNode->value){
if(focusNode->right == NULL){
focusNode->right = new bstNode(value);
return;
} else focusNode = focusNode->right;
}
else{
if(focusNode->left == NULL){
focusNode->left = new bstNode(value);
return;
} else focusNode = focusNode->left;
}
}
I also fixed your printBST function. In the printBST function use if instead of while, because the the code inside the while loop would be executed an infinite number of times instead of printing the BST once.
static void printBST(bstNode* node){
if(node != NULL){
printBST(node->left);
cout << node->value <<" ";
printBST(node->right);
}
}
I wanted to implement a BST class with a vector and somehow its not working. I just wanted to know the reason why its not working.
The main reason that I can think of that root in the BST always remain NULL.
I wanted to experiment ways to use classes in data structures.
#include<iostream>
#include<vector>
using namespace std;
class Node{
public:
int data;
Node* left ;
Node* right ;
Node(int val){
data = val;
left = NULL;
right = NULL;
}
};
class BST{
public:
Node* root = NULL;
void insert(Node* r,int data){
Node* new_node = new Node(data);
if(r == NULL){
r = new_node;
}
if(data < r->data){
if(r->left == NULL){
r->left = new_node;
}
else{
insert(r->left,data);
}
}else if(data > r->data){
if(r->right == NULL){
r->right = new_node;
}
else{
insert(r->right,data);
}
}else{
return;
}
return;
}
BST(vector<int> bst_array){
for(int i = 0; i<bst_array.size(); i++){
insert(root,bst_array[i]);
}
}
void print_t(Node* r){
if(r == NULL){
cout<<"NULL";
return;
}
else{
print_t(r->left);
cout<<r->data<<" ";
print_t(r->right);
}
}
};
int main(){
vector<int> v = {1,3,5,44,23,78,21};
BST* tr = new BST(v);
tr->print_t(tr->root);
return 0;
}
There seem to be a logical mistake on my end please help me find it.
Thanks in advance.
The reason is that root is never assigned another value after its initialisation to NULL. Passing root as argument to the insert method can never alter root itself, as it is not the address of root that is passed, but its value.
Some other remarks:
insert always starts by creating a new node, at every step of the recursion. This is a waste of node creation. In the end you just need one new node, so only create it when its position in the tree has been identified.
The final else is not needed, as all it does is execute a return, which it would have done anyway without that else block
As insert is a method of BST, it is a pity that it requires a node as argument. You would really like to just do insert(data) and let it take care of it. For that to happen I suggest to move your insert method to the Node class, where the this node takes over the role of the argument. Then the BST class could get a wrapping insert method that forwards the job to the other insert method.
Instead of NULL use nullptr.
To solve the main issue, there are many solutions possible. But after making the above changes, it is quite easy to assign to root in the simplified insert method on the BST class.
Here is how it could work:
class Node{
public:
int data;
Node* left ;
Node* right ;
Node(int val){
data = val;
left = nullptr;
right = nullptr;
}
void insert(int data) {
if (data < this->data) {
if (this->left == nullptr) {
this->left = new Node(data);
} else {
this->left->insert(data);
}
} else if (data > this->data) {
if (this->right == nullptr) {
this->right = new Node(data);
} else {
this->right->insert(data);
}
}
}
};
class BST {
public:
Node* root = nullptr;
void insert(int data) {
if (root == NULL) { // Assign to root
root = new Node(data);
} else { // Defer the task to the Node class
root->insert(data);
}
}
BST(vector<int> bst_array){
for(int i = 0; i<bst_array.size(); i++){
insert(bst_array[i]); // No node argument
}
}
/* ...other methods ...*/
}
I have been trying to get this function working for the longest time now. It is part of an assignment for an online course, but it seems no matter what I submit, the function fails for both the empty child test and the left child test. See code below. The main() function is deliberately commented out. Any info./input is much appreciated.
// C++ binary trees and stuff;
//
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
class BST
{
public:
int data;
BST *left;
BST *right;
//BST *root;
// BST() constructor
BST (int num)
{
data = num;
left = nullptr;
right = nullptr;
root = nullptr;
}
// constructors for root node(s), initializing as root when no values exist yet;
BST() : root (nullptr){}
BST (BST *rootNode) : root(rootNode){}
void insert (int value)
{
BST *newNode = new BST();
newNode = root;
if (root == nullptr)
{
root = new BST (value);
}
else
{
root->data = value;
}
// check if newNode's value equals the passed-in value:
if (value == root->data)
{
//cout << "\nWarning! Value already exists in tree, so nothing will be done.\n";
return;
}
// check if value is < or > newNode's value:
if (value <= root->data)
{
if (root->left == nullptr)
{
// make a new node as the left child of this node,
root->left = new BST(value);
}
else
{
// recursively call insert() on tree's left side,
root->left->insert(value);
}
}
else
{
if (root->right == nullptr)
{
// make a new node as the right child of this node,
root->right = new BST(value);
}
else
{
// recursively call insert() on tree's right side,
root->right->insert(value);
}
}
}
public:
BST *root;
};
/*
int main (int argc, char *argv[])
{
//...insert code here,
// create nodes,...
BST rootNode(5);
BST leftNode(4);
BST rightNode(6);
// connect the nodes to the tree via rootNode.left and rootNode.right,..
rootNode.left = &leftNode;
rootNode.right = &rightNode;
printf ("\nData (root) value = %i, rootNode.left = %i, and rootNode.right = %i\n",
rootNode.data, rootNode.left->data, rootNode.right->data);
cout << "\n\nHello, Solar System!\n";
return 0;
}
*/
Okay, here's my suggestion. You need to reformat your code. You need two classes. You need a BST, and you need a Node. The various methods to add/remove/traverse are part of the BST class. Nodes are just Nodes.
So:
class BST_Node {
public:
int value;
BST_Node * left = nullptr;
BST_Node * right = nullptr;
// Define constructors, etc.
};
class BST {
public:
BST_Node * root = nullptr;
BST_Node * insert(int value);
void insertNode(BST_Node *node);
void insertNodeBelow(BST_Node *nodeToInsert, BST_Node *startingNode);
};
BST_Node * BST::insert(int value) {
BST_Node * node = new BST_Node(value);
insertNode(node);
return node;
}
void BST::insertNode(BST_Node *node) {
if (node == nullptr) {
return;
}
if (root == nullptr) {
root = node;
}
else {
insertNodeBelow(node, root);
}
}
void BST::insertNodeBelow(BST_Node *node, BST_Node *startingNode) {
if (node == nullptr || startingNode == nullptr) {
return;
}
if (node->value < startingNode->value) {
if (startingNode->left != nullptr) {
insertNodeBelow(node, startingNode->left);
}
else {
startingNode->left = node;
}
}
else {
if (startingNode->right != nullptr) {
insertNodeBelow(node, startingNode->right);
}
else {
startingNode->right = node;
}
}
}
How this works... First, the logic of how to store nodes is in BST. Nodes don't care. Second, I made methods for either inserting a value or a node. Because I think that's handy. That should be fairly easy to understand.
The root node can be null, if so, then your inserted node is now root. Otherwise it calls the recursive insertion function. Now, you could simplify this a little, but I didn't want to get too clever.
So it's simple. We look to see where it belongs relative to the point we're at (initially the root). Either we go into the left branch or the right branch. But that branch could be empty, so you just plop it right in. If it's not empty, then you recurse.
I didn't test it.
So it's very hard to explain so I'll just show it to you. I'm trying to implement a Red-Black tree in C++. Below is the code relevant to the question (complete code at this link)
I'm fairly new so please forgive me if I don't use the right terminology.
My question is about creating the root node. When a new value is added using addValue, it's assigning the new node to the root but at the same time is passing the root in as the parameter. I don't get any errors but it feels like this is not a good way to go about it.
enum colour {RED, BLACK, DOUBLEBLACK};
struct Node{
int data;
int colour;
Node *left, *right, *parent;
explicit Node(int);
};
class Tree{
public:
Tree();
virtual ~Tree(){};
void addValue(int);
Node* insertNode(Node *, Node*);
private:
Node* root;
};
Node::Node(int data) {
this->data = data;
colour = RED;
left = right = parent = nullptr;
}
Tree::Tree() {
root = nullptr;
}
void Tree::addValue(int n) {
Node *node = new Node(n);
root = insertNode(root, node); //*********** this line here
insertFix(node);
}
Node* Tree::insertNode(Node* root, Node* node) {
if (root == nullptr)
return node;
if(node->data < root->data) {
root->left = insertNode(root->left, node);
root->left->parent = root;
} else if (node->data > root->data) {
root->right = insertNode(root->right, node);
root->right->parent = root;
}
return root;
}
typedef struct treeNode {
treeNode* left;
treeNode* right;
int data;
treeNode(int d) {
data = d;
left = NULL;
right = NULL;
}
}treeNode;
void insert(treeNode *root, int data) {
if (root == NULL) {
cout << &root;
root = new treeNode(data);
}
else if (data < root->data) {
insert(root->left, data);
}
else {
insert(root->right, data);
}
}
void inorderTraversal(treeNode* root) {
if (root == NULL)
return;
inorderTraversal(root->left);
cout<<root->data;
inorderTraversal(root->right);
}
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
insert(root, 2);
inorderTraversal(root);
return 0;
}
So I'm pretty tired, but I was whipping some practice questions up for interview prep and for some reason this BST insert is not printing out that any node was added to the tree. Its probably something im glossing over with the pointers, but I can't figure it out. any ideas?
void insert(treeNode *root, int data) {
if (root == NULL) {
cout << &root;
root = new treeNode(data);
}
This change to root is lost as soon as the function ends, it does not modify the root passed as argument but its own copy of it.
Take note that when u insert the node, use pointer to pointer (pointer alone is not enough):
So, here is the fixed code:
void insert(treeNode **root, int data) {
if (*root == NULL) {
cout << root;
*root = new treeNode(data);
}
else if (data < (*root)->data) {
insert(&(*root)->left, data);
}
else {
insert(&(*root)->right, data);
}
}
And in main:
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
insert(&root, 2);
inorderTraversal(root);
return 0;
}
Your logic is correct!
The only issue is that when you create a local variable, even if it is a pointer, its scope is local to the function. In your main:
...
insert(root, 2);
...
function call sends a copy of the root which is a pointer to treeNode (not the address of root). Please note that
void insert(treeNode *root, int data)
gets a treeNode pointer as an argument (not the address of the pointer). Attention: This function call may look like "call by pointer" (or reference) but it is actually "call by value". The root you define in the main function and the root inside the insert method have different addresses in the stack (memory) since they are different variables. The former is in main function stack in the memory while the latter is in insert method. Therefore once the function call insert finishes executing, its stack is emptied including the local variable root. For more details on memory refer to: stacks/heaps.
Of course the data in the memory that you allocated using:
*root = new treeNode(data);
still stays in the heap but you have lost the reference to (address of) it once you are out of the insert function.
The solution is either passing the address of original root to the function and modifying it (as K-ballo and dip has suggested) OR returning the modified local root from the function. For the first approach please refer to the code written by dip in his/her answer.
I personally prefer returning the modified root from the function since I find it more convenient especially when implementing other common BST algorithms. Here is your function with a slight modification of your original code:
treeNode* insert(treeNode *root, int data) {
if (root == NULL) {
root = new treeNode(data);
}
else if (data < root->data) {
root->left=insert(root->left, data);
}
else {
root->right=insert(root->right, data);
}
return treeNode;
}
The function call in main will be:
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
root = insert(root, 2);
inorderTraversal(root);
return 0;
}
Hope that helps!
After a while seeing some complicated methods of dealing with the Binary tree i wrote a simple program that can create, insert and search a node i hope it will be usefull
/*-----------------------Tree.h-----------------------*/
#include <iostream>
#include <queue>
struct Node
{
int data;
Node * left;
Node * right;
};
// create a node with input data and return the reference of the node just created
Node* CreateNode(int data);
// insert a node with input data based on the root node as origin
void InsertNode (Node* root, int data);
// search a node with specific data based on the root node as origin
Node* SearchNode(Node* root, int data);
here we define the node structure and the functions mentioned above
/*----------------------Tree.cpp--------------*/
#include "Tree.h"
Node* CreateNode(int _data)
{
Node* node = new Node();
node->data=_data;
node->left=nullptr;
node->right=nullptr;
return node;
}
void InsertNode(Node* root, int _data)
{
// create the node to insert
Node* nodeToInsert = CreateNode(_data);
// we use a queue to go through the tree
std::queue<Node*> q;
q.push(root);
while(!q.empty())
{
Node* temp = q.front();
q.pop();
//left check
if(temp->left==nullptr)
{
temp->left=nodeToInsert;
return;
}
else
{
q.push(temp->left);
}
//right check
if(temp->right==nullptr)
{
temp->right=nodeToInsert;
return;
}
else
{
q.push(temp->right);
}
}
}
Node* SearchNode(Node* root, int _data)
{
if(root==nullptr)
return nullptr;
std::queue<Node*> q;
Node* nodeToFound = nullptr;
q.push(root);
while(!q.empty())
{
Node* temp = q.front();
q.pop();
if(temp->data==_data) nodeToFound = temp;
if(temp->left!=nullptr) q.push(temp->left);
if(temp->right!=nullptr) q.push(temp->right);
}
return nodeToFound;
}
int main()
{
// Node * root = CreateNode(1);
// root->left = CreateNode(2);
// root->left->left = CreateNode(3);
// root->left->left->right = CreateNode(5);
// root->right = CreateNode(4);
// Node * node = new Node();
// node = SearchNode(root,3);
// std::cout<<node->right->data<<std::endl;
return 0;
}