Binary Search Tree: root remains null during Inorder (C++) - c++

So I'm a little new to Binary search trees and I'm trying to make a binary tree where each node is a vector of strings. then each insertion takes a string and only considers the first letters of that string. Based off the first 2 letters it will either append that string to an existing node where all string share the same 2 first letters or create a new node which will hold a vector of strings with all the same 2 first letters. Weird I know. It wasn't my idea.
I've tried narrowing down where the issue is by displaying the root at every insertion. And the insertions all seem to be working fine, but as soon as I want to display the nodes in Inorder, the root just seems to disappear, BUT almost like it's invisible. It's very evident base on the output. My guess is that it's null but I'm not sure. Sorry if this isn't the best way to ask. this is my first question here.
here's my code:
#include <iostream>
#include <string>
#include <vector>
//#include "stringSlicer.h"
using namespace std;
class BST
{
vector<string> data;
BST *left, *right;
public:
// Default constructor.
BST();
// Parameterized constructor.
BST(string);
// Insert function.
BST* Insert(BST*, string);
// Inorder traversal.
void Inorder(BST*);
// PreOrder Traversal.
void PreOrder(BST*);
// PostOrder Traversal
void PostOrder(BST*);
// string slicer
string strSlice(string);
// print vector
void printVector(vector<string>);
};
// Default Constructor definition.
BST ::BST()
: data(0)
, left(NULL)
, right(NULL)
{
}
// Parameterized Constructor definition.
BST ::BST(string value)
{
if(data.empty()){
data.push_back(strSlice(value));
}
data.push_back(value);
left = right = NULL;
}
// String slicing function definition
string BST ::strSlice(string word){
string word2 = "";
word2 += word[0];
word2 += word[1];
return word2;
}
// print vector function definition
void BST ::printVector(vector<string> dataVector){
for(int i = 0; i < dataVector.size(); i ++){
cout << dataVector.at(i) << " ";
cout << "end of this node";
}
}
// Insert function definition.
BST* BST ::Insert(BST* root, string value)
{
if (!root)
{
// Insert the first node, if root is NULL.
return new BST(value);
}
// Insert data.
if (strSlice(value).compare(root->data.at(0)) > 0)
{
// Insert right node data, if the 'value'
// to be inserted is greater than 'root' node data.
cout << value << " is being put in the right node " << value << " > " << root->data.at(0) << endl;
// Process right nodes.
root->right = Insert(root->right, value);
} else if (strSlice(value).compare(root->data.at(0)) == 0) {
cout << value << " is being put in the same node " << value << " = " << root->data.at(0) << endl;
root->data.push_back(value);
}
else
{
// Insert left node data, if the 'value'
// to be inserted is greater than 'root' node data.
cout << value << " is being put in the left node " << value << " < " << root->data.at(0) << endl;
// Process left nodes.
root->left = Insert(root->left, value);
}
// Return 'root' node, after insertion.
cout << "after insert root is " << root << endl;
return root;
}
// Inorder traversal function.
// This gives data in sorted order.
void BST ::Inorder(BST* root)
{
cout << "root is " << endl;
if (!root) {
return;
}
Inorder(root->left);
printVector(data);
cout << endl;
Inorder(root->right);
}
int main() {
const int size = 5;
string array [size] = {"hi","hillo","bye","chao","elo"};
BST b, *root = NULL;
cout << "root is " << root << endl;
root = b.Insert(root, array[0]);
for (int i = 1; i < size; i ++){
b.Insert(root, array[i]);
}
b.Inorder(root);
return 0;
}
here was the output:
root is 0
hillo is being put in the same node hillo = hi
after insert root is 0xeb7f10
bye is being put in the left node bye < hi
after insert root is 0xeb7f10
chao is being put in the left node chao < hi
chao is being put in the right node chao > by
after insert root is 0xeb7f30
after insert root is 0xeb7f10
elo is being put in the left node elo < hi
elo is being put in the right node elo > by
elo is being put in the right node elo > ch
after insert root is 0xeb7f88
after insert root is 0xeb7f30
after insert root is 0xeb7f10
root is
root is
root is
root is
root is
root is
root is
root is
root is

Problem:
Your nodes are not NULL, but you're not printing the data of any of them. With the statement printVector(data); you're printing just the data of the object b.
Solution:
Change printVector(data); to printVector(root->data);.
Additional information:
using namespace std; is considered a bad practice (More info here).
Instead of creating an object b just to use the methods of the class BST, make the methods static and pass the node as an argument. It's cleaner and will help to avoid confusions as this case.
Personally I would recommend you to use nullptr instead of NULL in C++.
Even if root is NULL, the Inorder method will execute "cout << "root is " << endl;" before returning and therefore outputting unnecesary lines.
You should use delete to free the data you store with new.

Related

C++ How to store the nodes of an in order traversal into an array?

I have an in-order traversal function like such
void Inorder(node *root)
{
node* array;
array = new node[arraySize];
if (root == NULL)
return;
Inorder(root->left); //visit left sub-tree
std::cout << "Word: " << root->key << std::endl
<< "Occurance: " << root->count << std::endl; //print root key and its count
Inorder(root->right); //visit right sub-tree
}
In order to sort it any further I need to store the node transversed in an array however im not sure i can accomplish this. Visually i want something like this
node array[0] = transversedNode;
Ive tried adding root to the array
Ive tried adding Inorder(root->left) to the array when Inorder is of type node
But none of these accomplish what i need. Is it possible to store the nodes transversed into an array? Thankyou
As others commented already, you can replace your array declaration with an std::vector<Node*> and return it:
void InorderRecursive(Node *root, std::vector<Node*>& nodes)
{
if (root == NULL)
return;
InorderRecursive(root->left, nodes); //visit left sub-tree
std::cout << "Word: " << root->key << std::endl
<< "Occurance: " << root->count << std::endl; //print root key and its count
nodes.push_back(root);
InorderRecursive(root->right, nodes); //visit right sub-tree
}
std::vector<Node*> Inorder(Node *root)
{
std::vector<Node*> nodes;
InorderRecursive(root, nodes);
return nodes;
}
I added an additional function InorderRecursive that calls itself and is called by Inorder. It has std::vector<Node*> reference as an additional parameter. The original vector is constructed in Inorder and passed. This way you have a single container, to avoid copying the whole thing.

How do I properly delete a node from a linked list?

I've created a linked list of animal objects. When an animal reaches its lifespan I want to remove it from the the linked list. However, whenever I run my attempt, the previous node is not being set for the second deletion of the hyena object. And the tiger object which is the following node is being deleted even though it has the highest lifespan.
Specifically in line 6 of the output, I don't understand why possum is being used as the previous node instead of chicken.
NodePtr temp = head;
while (temp != NULL){
temp->data->setAge(temp->data->getAge()+1);
if (temp->data->getAge() > temp->data->getLifeSpan()){
std::cout << temp->data->getAnimal() << " lifespan reached" << std::endl;
if (temp->prev == NULL) { // remove head
head = temp->next;
delete(temp);
temp = NULL;
temp = head;
continue;
}
else {
std::cout << temp->prev->data->getAnimal() << " " << temp->data->getAnimal() << " " << temp->next->data->getAnimal() << " (prev node, curr node, next node)" << std::endl;
NodePtr nextPtr = temp->next;
temp = temp->prev;
std::cout << "Current Node: " << temp->data->getAnimal() << " \tNext Node: " << nextPtr->data->getAnimal() << std::endl;
if (temp == NULL || temp->next == NULL){
continue;
}
delete(temp->next);
temp->next = NULL;
NodePtr prev = temp;
temp = nextPtr;
continue;
}
}
temp = temp->next;
}
You might find std::remove_if useful. Here's a demo
animal.cpp
#include "animal.h"
animal::animal(std::string name, int age, int life_span) :
name(name),
age(age),
life_span(life_span)
{}
bool p(const animal& obj)
{
return (obj.getAge() > obj.getLifeSpan());
}
int main()
{
animal cat = { std::string("cat"), 5, 10 };
animal dog = { std::string("dog"), 20, 10 };
std::vector<animal> vec{cat, dog};
std::for_each(vec.begin(), vec.end(), [&](animal a) {
std::cout << "Animal type: " << a.getName() << ", Animal age: " << a.getAge() << ", Animal lifespan: " << a.getLifeSpan() << std::endl;
});
vec.erase(std::remove_if(vec.begin(), vec.end(), p), vec.end());
std::cout << " " << std::endl;
std::cout << "Removed old animals" << std::endl;
std::cout << " " << std::endl;
std::for_each(vec.begin(), vec.end(), [&](animal a) {
std::cout << "Animal type: " << a.getName() << ", Animal age: " << a.getAge() << ", Animal lifespan: " << a.getLifeSpan() << std::endl;
});
}
animal.h
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
class animal
{
public:
int getAge() const { return age; }
int getLifeSpan() const { return life_span; }
std::string getName() const { return name; }
animal(std::string name, int age, int life_span);
private:
std::string name;
int life_span;
int age;
};
Output:
Animal type: cat, Animal age: 5, Animal lifespan: 10 Animal type: dog,
Animal age: 20, Animal lifespan: 10
Removed old animals
Animal type: cat, Animal age: 5, Animal lifespan: 10
If I understand what you need to do, that you need to traverse the list and update the age for each animal and then remove the nodes for animals that have died of old age -- and you really do have to write your own linked-list, then the removal isn't difficult so long as you utilize both the address of the current node (a pointer-to-pointer) and a pointer to the current node. See Linus on Understanding Pointers
When you have both the address of the current node, and a pointer to the node, you simply update what is stored at that address with the next node in the list. That removes the node from the list without having to worry about tracking the previous node. Why? Because the previous node ->next pointer still points to that address.
Since you have a pointer to the node that was just removed from the list you simply delete that node and update the pointer to next node.
(note: the node is not actually "removed" from the list, it's place in the list is simply overwritten by the next node, effectively removing it from traversal)
Putting those pieces together, you can write your removal function as:
NodePtr *ptemp = &head, /* pointer-to-pointer to head */
temp = head; /* pointer to head */
while (temp) { /* traverse the list updating both ptemp and temp */
temp->data->setAge(temp->data->getAge()+1); /* update age */
/* if dead */
if (temp->data->getAge() > temp->data->getLifeSpan()) {
*ptemp = temp->next; /* fill current address with next node */
delete temp; /* delete node that was current */
temp = *ptemp; /* update temp pointer to current */
}
else { /* still alive */
ptemp = &temp->next; /* get address of next node as current */
temp = temp->next /* update node pointer to next */
}
}
(note: this assumes that NodePtr is already a typedef'ed pointer to your actual struct. You will want to review: Is it a good idea to typedef pointers?.)
The only caveat above is that a normal delete_node function will traverse the list until it finds a specific node that holds a given value to delete and the node is removed and traversal ends. In your case you want to traverse the entire list and check each animal's age, and remove each that has died of old-age. This requires that you not only check the age of the current node being removed, but also the node that you move into its place in the list.
What this translates into from a code standpoint is it adds an else clause to the function. In the case where the animal has died and you have moved the next node into its position in your list, you simply update the temp pointer to point to the node you have moved into the current position (temp->next) after the delete takes place. That way that node is checked for age on the next iteration.
The added else clause handles the normal traversal to the next node for the case where the animal at that node remains alive.
This code hasn't been compiled/tested (because there was no MCVE to work with), but should work as advertised. Give it a go and let me know if you have any problems and I'm happy to help further. (add the MCVE if I need to help further)

C++ Creating Binary Search Tree: EXC_BAD_ACCESS error. Bad Algorithm or coding error?

Question: I keep receiving exc_bad_access (process code 11) error. Is this due to a bad algorithm or simply a coding error? Can anyone help me fix it?
My class assignment is to create a binary search tree whose nodes can store a name, a balance, and a key. Nodes are to be organized and searched for using the key. This tree should support insertion, inorder traversal, and searching based on a key (I haven't built this function yet). I've also included several other functions to facilitate building these. If it matters, I'm using CLion on OSX High Sierra. Additionally, I get the error on the first prompt to enter node information, the error does not seem to be related to the input itself.
//Genghis Khan
#include <iostream>
#include <vector>
using namespace std;
class node
{
public:
int key;
string name;
double balance;
node *leftptr;
node *rightptr;
friend class tree;
};
class tree
{
public:
node *root, *temp, *v;
//Constructor
tree()
{
root = NULL;
temp = root;
}
bool empty()
{
return(root == NULL);
}
bool isleaf(node *x)
{
return((x->leftptr == NULL) && (x->rightptr == NULL));
}
void inorder(node *temp)
{
if(~isleaf(temp))
{
inorder(temp->leftptr);
cout << "Name: " << temp->name << " " << "Balance: " <<
temp->balance << " " << "Key: " << temp->key;
inorder(temp->rightptr);
}
}
node* createnode()
{
v = new node;
cout << "Enter name (string): " << endl;
getline(cin, v->name);
cout << "Enter key (integer): " << endl;
cin >> v->key;
cout << "Enter balance (double): " << endl;
cin >> v->balance;
return(v);
}
void set()
{
temp = root;
}
void insert(node *v)
{
while(~isleaf(temp))
{
if((v->key < temp->key))
{
temp = temp->leftptr;
insert(v);
}
else if(v->key > temp->key)
{
temp = temp->rightptr;
insert(v);
}
}
temp->key = v->key;
temp->balance = v->balance;
temp->name = v->name;
}
};
int main()
{
int n;
cout << "Enter number of people: ";
cin >> n;
//Creating instance of tree, inserting all data into tree
tree b;
for(int i = 0; i < n; i++)
{
b.set();
node *a = b.createnode();
b.insert(a);
}
//inorder part
b.set();
b.inorder(b.temp);
}
The functions are (pseudocode):
1. function isleaf(x): return(x's left pointer and x's right pointer are both NULL)
2. function set(): set temp to root //temp will be reset every time an insertion, traversal, or search occurs
3. function createnode():
v is a new node
get all the fields for v
return v
4. function insert(v)
while(not isleaf(temp)):
-if(v's key < temp's key)
temp = temp's left pointer (to the lower value child node)
insert(node *v)
-if(v's key > temp's key)
temp = temp's right pointer (to the higher value child node)
insert(node *v)
end while
duplicate v's data to temp, now that temp is a leaf
5. function inorder(temp):
if(not isleaf(temp):
inorder(temp's left pointer)
output all info in temp node
inorder(temp's right pointer)
Main Algorithm
for the number of nodes to be entered:
1. set
2. node *a = createnode
3. insert(a)
Update
The error seems to be coming from the 'if((v->key < temp->key))' line.
EXC_BAD_ACCESS just means that you are trying to access an invalid memory. By a quick brief, your function isleaf doesn't check whether x is null.
May have other errors, you can debug and find it by yourself.

How to fix logic errors in my binary search tree?

so I have been trying to get an old c++ binary search tree program of mine to work.It compiles and runs but I do not get the results I would expect. If I insert c,d,a,b in that order and try to remove c, my remove function skips the if conditionals that find in order successors. Why are those 2 else if conditionals skipped?
Also it is compiled using gcc.
Node::Node(string nodeItem,
int nodeLine){
item=nodeItem;
vector<int> tempVector;
tempVector.push_back(nodeLine);
lines=tempVector;
leftPtr = NULL;
rightPtr = NULL;
}
// recursive method for finding node containing the word
Node* BST::find(string data, Node *curr) {
if(curr==NULL) {
cout << data << " is not in the tree" << endl;
return curr;
}
if(curr->getItem().compare("theplaceholder")==0){
return curr;
}
string tempItem = curr->getItem();
//this if statement is if I am inserting a word that is already in the tree
// or if I am removing the word from the tree
if(data.compare(tempItem)==0){
return curr;
}
else if(data.compare(tempItem)<0){
return find(data,curr->getLeftPtr());
}
else{
return find(data, curr->getRightPtr());
}
}
void BST::insert(string data, int fromLine) {
Node *curr;
curr=find(data, root);
if(curr!=NULL && curr->getItem().compare("theplaceholder")==0){
curr->setData(data);
curr->addLines(fromLine);
}
if(curr==NULL){
// I want to point to a nonNull node.
// I am making a new node and having curr point to that instead of NULL
//then I set it to
curr=new Node(data, fromLine);
cout <<curr->getItem() << endl;
vector<int> foundLines=curr->getNodeLines();
//cout<< "The word " <<curr->getItem() << " can be found in lines ";
if(foundLines.empty())
cout << "foundLines is empty";
int size=foundLines.size();
for(int count=0; count<size; count++){
//cout << foundLines[count] << ", ";
}
}
if(curr->getItem()==data){
curr->addLines(fromLine);
}
}
// remove method I am trying to check for in order successors to swap with the deleted node.
void BST::remove(string data) {
Node *curr=root;
Node *temp=find(data, curr);
if(temp==NULL){
cout << " nothing to remove" << endl;
}
else if(temp->getRightPtr()!=NULL){
curr=temp->getRightPtr();
cout << curr->getItem() << endl;
while(curr->getLeftPtr()!=NULL){
curr=curr->getLeftPtr();
cout << curr->getItem() << endl;
}
temp->setData(curr->getItem());
temp->setLines(curr->getNodeLines());
delete curr;
curr=NULL;
}
else if(temp->getLeftPtr()!=NULL){
cout <<"if !temp->getLeftPtr" << endl;
curr=temp->getLeftPtr();
cout << curr->getItem() << endl;
while(curr->getRightPtr()!=NULL){
curr=curr->getRightPtr();
cout << curr->getItem() << endl;
}
temp->setData(curr->getItem());
temp->setLines(curr->getNodeLines());
delete curr;
curr=NULL;
}
else{
cout <<"else delete temp" << endl;
delete temp;
temp=NULL;
}
}
The reason this line
else if(temp->getRightPtr()!=NULL){
never succeeds is that you never set the right pointer on any node - getRightPtr can only return null. If you'd examined the state of your tree in the debugger after you'd built it or if you stepped through the insert function you'd probably have seen this. The problems are:
your find function doesn't return null if the node isn't in the tree, whereas your insert function expects it will
your insert function needs to locate the position in the tree where this node should be - either through fixing the find function or on its own, then create a new node AND add a reference to it from the parent node, on either the left or right side as appropriate
your insert function appears the line number to the first-inserted node twice: once when you overwrite the placeholder and once at the end of insert (rather than use a placeholder here I'd probably have initialised root to be null and instead set root = curr when you create the first node)
your remove function needs to do more work when promoting the highest node from the left-hand branch; it needs to
correctly clean up that node from it's previous parent - at the moment you delete the object but leave any dangling pointers alone
promote any children of that node before you move it to take its previous slot
i.e.
D C
/ \ / \
A E remove 'D' A E
\ => 'C' is highest on left \
C but need to move B to C B
/
B

Expression Tree printLevel() function

I am implementing a tree which is a Binary Expression Tree. The leaf nodes are numbers, non-leaf nodes are math operators. Succesfully implemented printInorder,PostOrder, PreOrder, evaluate. But stucked with the printLevel().
Here is my int main ()
int main()
{
EXTree myTree;
string tests[] = {"2.1*3.1+4.2", "(2.0+1.3)/1.4", "2.*(1.3+1.4)","1.2*(1.3+1.4/0.5)","1.2*(1.3+1.4/0.5)-4.4", "1.2*(1.3+1.4/0.5)- (9/3)"};
for (int i=0; i < 6; i++)
{
myTree.build (tests[i]);
myTree.printInorder();
myTree.printPreorder();
myTree.printPostorder();
myTree.printLevel(); //Starting from level = 0
cout << "Evaulating myTree = " << format(myTree.evaluate(),2) << endl;
myTree.removeAll(); // removes all the nodes
}
}
printLevel(); only prints the level of the tree given above and its initally 0.
and here is my printLevel function.
void EXTree:: printLevel()
{
queue<Node*> levelq;
levelq.push(root);
cout << "Current Level is: ";
while( levelq.size() > 0 )
{
Node *cur = levelq.front();
cout << cur->Root << " ";
levelq.pop();
if (cur->Left) levelq.push(cur->Left);
if (cur->Right) levelq.push(cur->Right);
}
cout << endl;
}
But I really didnt understand how to implement the printLevel. Appreciate for any help to clarify it.
I just implemented the inOrder algorith to my printLevel and tried to change it but still didnt get it.
Since you have no problem with recursion, this would work without queue:
void EXTree:: printLevel()
{
int currentLevel = 0;
if (root)
{
cout << "Current Level is: ";
printLevelHelper(root,currentLevel);
}
else
cout << "This BST is Empty\n";
}
// Declare a private method:
void EXTree:: printLevelHelper(Node* &n, int &currentLevel)
{
cout << currentLevel << ' ';
if (n->Left)
{
currentLevel++;
printLevelHelper(n->Left,currentLevel);
currentLevel--;
}
if (n->Right)
{
currentLevel++;
printLevelHelper(n->Right,currentLevel);
currentLevel--;
}
}
When using Breadth First Search to print the nodes on one level immediately adjacent to each other, you'd just observe when the leftmost child of the leftmost node on the current level pops out of the queue: this must be the start of the next level. I could easily write the code but I'd guess it would incomprehensible for you and this homework is for you (I think you want to label your post appropriately as homework, BTW). Most of your implementation looks like a straight forward implementation. The only thing missing is detecting that the next level is reached.