How to fix logic errors in my binary search tree? - c++

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

Related

Binary Search Tree: root remains null during Inorder (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.

How to initialize a pointer appropriately to avoid segmentation fault in c++?

I have started learning c++ (coming from java background) and barely reached pointers and got stuck. When I debug this program it says, program received segmentation fault (SIGSEGV signal) at line
*previous = head;
in the following code.
#include <iostream>
using namespace std;
struct Node
{
int data;
Node *link;
};
int main()
{
cout << "Starting main program \n";
Node head;
head.data = 0;
head.link = NULL;
cout << "After declaring head and initializing values \n";
//Declaring a pointer variable which points to an entity of type struct.
Node *previous;
*previous=head;
cout << "After declaring previous pointer \n";
bool done = false;
int i = 1;
cout << "First while loop\n";
while(!done)
{
cout << i << ": Iteration";
Node temp;
temp.data=i;
temp.link=NULL;
if(i > 2)
{
done = true;
continue;
}
*previous->link=temp;
++i;
*previous = temp;
}
done = false;
cout << "Declaring temp pointer before printing \n";
Node *temp;
*temp = head;
cout << "Second while loop\n";
while (!done)
{
cout << i << ": Iteration";
if(temp == NULL)
{
done = true;
continue;
}
cout << temp->data << "->";
*temp = *temp->link;
}
cout << "NULL";
}
Why is the pointer initialization incorrect ?
First problem:
Node *previous;
*previous=head;
First line declares that previous will hold the address of a Node. It is not initialized, so whatever value happens to be on the stack will be picked up as the bit pattern it holds.
Unfortunately, the 2nd line then dereferences the pointer (which points to garbage) and attempts to copy head into random memory (hence your crash).
In this case you probably want previous to point to head, which is taking head's address and assigning it:
Node* previous = &head; // initialize at the point of declaration
However, you must also be very wary of pointers to variables declared on the stack, because the addresses will soon become invalid when the function returns or the scope exits.
(Usually data structures with pointers are using values allocated on the heap, so the objects outlive the function that declares them.)
Which brings us to the second problem:
while(!done)
{
cout << i << ": Iteration";
Node temp;
Already there's a problem. temp is declared inside the loop on the stack. Each loop iteration, the variable will automatically be destroyed. Therefore it cannot participate in your linked list without corrupting it.
You want your list nodes to be created with new, and when you update previous's next pointer, you want to assign an address TO it, not copy an object THROUGH it.
Something like this:
while(!done)
{
cout << i << ": Iteration";
Node * temp = new Node();
temp->data = i;
temp->link = nullptr; // better than NULL
if(i > 2)
{
break;
}
previous->link = temp;
++i;
previous = temp;
}
The head object should probably also be heap allocated. Of course, now you have to deal with cleaning up the memory by calling delete on all the nodes.
There were some bugs in the code but major ones were :-
you were not allocating memory for the new nodes that you were adding during runtime
you were creating instances of structure but instead you were required to create an pointer pointing to the structure ( instances will be created during runtime ( using new operator )
I have added the comments to the code explaining what exactly are the changes that I have done.
Here is the fix :-
#include <iostream>
using namespace std;
struct Node
{
int data;
Node *link;
};
int main()
{
cout << "Starting main program \n";
// Allocating memory for the new instance of Node and making "head" pointing to it
Node *head = new Node;
head->data = 0;
head->link = NULL;
cout << "After declaring head and initializing values \n";
//Declaring a pointer variable which points to an entity of type struct.
Node *previous;
// As head and previous both are pointers thus can be assigned as it is
previous = head;
cout << "After declaring previous pointer \n";
bool done = false;
int i = 1;
cout << "First while loop\n";
while(!done)
{
cout << i << ": Iteration";
// Allocating memory for the new instance of Node and making temp pointing to it
Node *temp = new Node;
// As temp is a pointer thus using member access ("- >") operator to access the members
temp->data=i;
temp->link=NULL;
if(i > 2)
{
done = true;
continue;
}
previous->link = temp;
++i;
previous = temp;
}
done = false;
cout << "Declaring temp pointer before printing \n";
Node *temp;
temp = head;
cout << "Second while loop\n";
while (!done)
{
cout << i << ": Iteration";
if(temp == NULL)
{
done = true;
continue;
}
cout << temp->data << "->";
temp = temp->link;
}
cout << "NULL";
}

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.

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.

C++ Stacked List push function printing next/previous element

I just had a question on printing out the previous value. The code prints out everything fine for when it pushes on top of the stack but when I try to output what was the previous head it just points to a memory address.
void linkedListStack::push(int v)
{
listNode *newNode = new listNode;
newNode->value = v;
newNode->next = NULL;
if (top != NULL)
{
newNode->next = top;
}
top = newNode;
cout <<"Pushed "<< newNode->value << " onto the top of the stack." << endl;
cout << "Previous is: " << newNode->next << endl;
}
The push function is reading integers from a file and pushing that data onto the stack, but I'm unsure how to get the next/previous value to be printed out.
edit:
newNode->next->value
has been tried but is broken, still.
newNode->next is a pointer to the next element in the list. This is naurally a memory address. So,
cout << "Previous is: " << newNode->next << endl;
prints out the memory address of the next element in the stack. What you needed is the value of the next element. When ever you have a pointer to an object and you need to access something within the object, you use the arrow operator (->). So your code should have been:
cout << "Previous is: " << newNode->next->value << endl;
Also what if there was no next element (Always the case when you insert the first elemnt)? In your case, newNode->next would then be NULL, leading to undesired result when you first enter the element. So you need to check if it is NULL:
if (newNode->next != NULL)
{
cout << "Previous is: " << newNode->next->value << endl;
}
else
{
cout << "No previous element" << endl;
}
Output will break trying to access the new node in the case where it's the first pushed on the stack (you'd be accessing nullptrs). Test for null before trying to print.
Edit: This answer should be taken in conjunction with LogicStuff's advice.
If newNode->value is the value and if newNode->next is the next node, newNode->next->value is presumably the value of the next (previous head or top) node.
You also can't print the value of the next node, if there isn't any. So do a check:
if(newnode->next)
cout << "Previous is: " << newNode->next->value << endl;