So I've been working on a project for school for sometime, and I've run up against a wall. My add_node function isn't working correctly, and I know why. What I'm trying to do is take in a file with multiple randomly generated letters, and create trees out of them, then make a confirmation.
The thing is that it overwrites the same node, instead of making multiple nodes. I figured this out using Visual studios debugger, but I have no idea what to implement to fix it. What happens is that instead of having multiple nodes create a tree (like gagtttca), it makes one node and overwrites it. The node becomes g, then a, etc. How would I go about adding more nodes to the tree without overwriting it? The add_node function is the very last one.
#include "stdafx.h"
#include <iostream>
#include <stack>
#include <fstream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
class myTreeNode
{
public:
char Data;
myTreeNode *childA; //A's always go in child1
myTreeNode *childT; //T's always go in child2
myTreeNode *childC; //c's always go in child3
myTreeNode *childG; //G's always go in child4
};
class Tree
{
public:
myTreeNode * Root;
Tree()
{
Root = new myTreeNode;
Root->Data = '-';
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
}
bool add_a_word(string word);
bool is_this_word_in_the_tree(string word);
bool add_node(myTreeNode * parent, char letter);
bool add_words(vector<string> w);
};
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words);
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads);
bool write_out_the_vector_to_screen(vector<string> my_vector);
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name);
ofstream out;
int main()
{
out.open("my_results.txt");
vector<string> words_in_genome;
char * genome_file_name = "my_genome.txt";//make certain to place this file in the correct folder. Do not change path.
if (!get_words_from_the_file(genome_file_name, words_in_genome))
return 1;
Tree * trees = new Tree();
trees->add_words(words_in_genome);
char * reads_file_name = "reads.txt"; //make certain to place this file in the correct folder. Do not change path.
if (!get_the_reads_from_file(reads_file_name, reads_to_be_tested))
return 1;
for (int i = 0; i < reads_to_be_tested.size(); i++)
{
out <<reads_to_be_tested[i] << " " << trees->is_this_word_in_the_tree(reads_to_be_tested[i]);
}
cout << "All done" << endl;
//Write out a file named "myResults.txt".
//For each read, list its sequence and either "Yes" or "No".
//This will indicate if it does or doesn't map to the genome.
/** Used for debugging
cout << "words" << endl;
write_vector_to_screen(words);
write_vector_to_file(words,"testing.txt");
cout << "reads" << endl;
write_vector_to_screen(reads);
***/
out.close();
}
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words)
{
int i, j;
int len = 0;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "I could not find " << my_file_name << endl;
cout << "Check the location.\n";
return false;
}
char * my_word = new char[11];
while (in.peek() != EOF) { in >> my_word[0]; len++; }
in.clear(); in.close(); in.open(my_file_name);
for (i = 0; i<10; i++)
{
in >> my_word[i];
if (my_word[i]<97) my_word[i] += 32; //makes it lowercase
}
my_word[10] = '\0';
vector_of_words.push_back(my_word);
for (i = 1; i<(len - 10 - 1); i++) //read until the end of the file
{
//shift
for (j = 0; j<9; j++) my_word[j] = my_word[j + 1];
in >> my_word[9];
if (my_word[9]<97) my_word[9] += 32; //makes it lowercase
my_word[10] = '\0';
cout << i << "\t" << my_word << endl; cout.flush();
vector_of_words.push_back(my_word);
}
in.clear(); in.close();
return true;
}
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads)
{
int i;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "The read file " << my_file_name << " could not be opened.\nCheck the location.\n";
return false;
}
char * word = new char[20]; //this is a default, we'll be looking at words of size 10
while (in.peek() != EOF)
{
in.getline(word, 20, '\n');
for (i = 0; i<10; i++) { if (word[i]<97) word[i] += 32; } //makes it lowercase
reads.push_back(word);
}
in.clear(); in.close();
delete word;
return true;
}
bool write_out_the_vector_to_screen(vector<string> my_vector)
{
int i;
for (i = 0; i < my_vector.size(); i++)
{
cout << my_vector[i].c_str() << endl;
}
return true;
}
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name)
{
ofstream out;
out.open(my_file_name);
int i;
for (i = 0; i<my_vector.size(); i++)
out << my_vector[i].c_str()<< endl;
out.clear();
out.close();
return true;
}
bool Tree::add_words(vector<string> w)
{
for (int i = 0; i < w.size(); i++)
add_a_word(w[i]);
return true;
}
bool Tree::add_a_word(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
tempNode = tempNode->childA;
else
{
add_node(tempNode, word[i]);//add a node: what letter, who's my parent
tempNode = tempNode->childA;
}
}
else if (word[i]== 'g')
{
if (tempNode->childG != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childG;
}
}
else if (word[i] == 'c')
{
if (tempNode->childC != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childC;
}
}
else if (word[i] == 't')
{
if (tempNode->childT != NULL)
tempNode = tempNode->childT;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childT;
}
}
else
{
cout << "The tree is full, or can't find data" << endl;
return NULL;
break;
}
}
}
}
}
bool Tree::is_this_word_in_the_tree(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
char com1, com2, com3, com4;
if (tempNode == NULL)
{
cout << "The tree is empty. Sorry" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
{
if (tempNode->childA)
{
tempNode = tempNode->childA;
com1 = 'y';
}
}
else
{
com1 = 'n';
}
}
if (word[i] == 'g')
{
if (tempNode->childG != NULL)
{
if (tempNode->childG)
{
tempNode = tempNode->childG;
com2 = 'y';
}
}
else
{
com2 = 'n';
}
}
if (word[i] == 't')
{
if (tempNode->childT != NULL)
{
if (tempNode->childT)
{
tempNode = tempNode->childG;
com3 = 'y';
}
}
else
{
com3 = 'n';
}
}
if (word[i] == 'c')
{
if (tempNode->childC != NULL)
{
if (tempNode->childC)
{
tempNode = tempNode->childC;
com4 = 'y';
}
}
else
{
com4 = 'n';
}
}
}
out << com1 << com2 << com3 << com4 << endl;
if (com1 == com2 == com3 == com4)
{
out << "The test passed" << endl;
}
else
{
out << "The test failed" << endl;
return false;
}
}
}
return true;
}
bool Tree::add_node(myTreeNode * parent, char letter)
{
//Can't figure out how to fix error. Run-Time error is that it overwrites the node instead of adding it.
//How would i make it so it's a new node every time?//
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Like I stated before, this is a project. I'm not here looking for an easy way out. I just want learn how to fix my code.
The most fundamental problem in your code is the simple obviousness that you're not comfortable using pointers. From the looks of it you may have come from other languages where the vernacular of:
Type *p = new Type;
p = Something;
was common. It is anything-but-common in C++. As in C, dynamic allocation is managed by a returned address, which is saved, cared for, and if all goes well, eventually disposed of. Those addresses are kept in pointer variables. Pointers in C++ don't hold objects; they hold addresses.
That said, I'm not going to destroy everything you wrote. I'm not going to sugar coat this; it would be shooting fish in a barrel. I'm rather going to describe what you should be doing in add_node, show you where you went wrong, and finally proffer up a simple example that eliminates much of the cruft (file io, etc) in your existing code, focusing rather on the real problem at hand: tree node management and the pointer-jockeying that is needed to accomplish it.
The Task
You should be starting at the root node, and for each successive letter in your string, move down the tree. When you encounter a path you want to take, but can't because there is no node hanging there yet, that is when you allocate a new node, hang it, move to it, and continue the process until there are no more characters in your input string.
Your Code
That said, review the comments in the following
bool Tree::add_node(myTreeNode * parent, char letter)
{
myTreeNode * tempNode = new myTreeNode;
// this is outright wrong. you just leaked the memory
// you allocated above. this has no place here and
// should be removed.
//
// Note: the remainder of this analysis will assume you
// have, in fact, removed this line.
tempNode = Root;
// all of this belongs in your myTreeNode constructor.
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
// this is flat-out impossible. Assuming you fixed your incorrect
// Root assignment mentioned above, you just allocated a new node
// therefore this can NEVER be NULL (an exception would have thrown
// on a failure to allocate).
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
// This NEVER changes. Nowhere in the code below this is
// tempNode ever assigned a different value. this loop
// should not even be here. A simple if-else-if stack or
// a switch on letter is all that is needed.
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Sample Code
The following strips out all the file io, and most of the insanity regarding managing the tree. There are only two member functions, add_word and has_word (the latter used to validate something is indeed present).
What makes this code work is how a pointer-to-pointer is used in the addition and check functions add_word and has_word. For addition, we start at the root node pointer, and with each successive character in the input string, move down the tree. When a child pointer is hit that is NULL, we allocate a new node, hang it, and move on. The check function has_word does exactly the same thing, save for one difference: it doesn't hang new nodes. When it encounters a NULL where there shouldn't be one, it means something is wrong and the input word is not in the tree.
#include <iostream>
#include <random>
#include <string>
struct myTreeNode
{
char data;
myTreeNode *childA;
myTreeNode *childT;
myTreeNode *childC;
myTreeNode *childG;
myTreeNode( char c )
: data(c), childA(), childT(), childC(), childG()
{
}
~myTreeNode()
{
delete childA;
delete childT;
delete childC;
delete childG;
}
// squelch these
myTreeNode(const myTreeNode&) = delete;
myTreeNode& operator=(const myTreeNode&) = delete;
};
class Tree
{
private:
myTreeNode *Root;
public:
Tree() : Root( new myTreeNode('-')) { }
~Tree() { delete Root; }
// squelch these
Tree(const Tree&) = delete;
Tree& operator =(const Tree&) = delete;
// adds a given string into the tree if it isn't already there.
void add_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default:
std::cerr << "skipping unsupported char '" << c << "'\n";
}
if (!*pp)
*pp = new myTreeNode(c);
}
}
// returns true if the given string is in the tree
bool has_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default: // should never happen with proper input
return false;
}
if (!*pp)
return false;
}
return true;
}
};
////////////////////////////////////////////////////////////////////////////////
int main()
{
// setup a random device and some uniform distributions
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<> dchar(0,3);
std::uniform_int_distribution<> dlen(3,8);
// our restricted alphabet. random indexes for creating our
// strings will be coming by indexing with dchar(rng)
char s[] = {'a', 't', 'c', 'g' };
// build set of random strings
std::vector<std::string> strs;
for (int i=0; i<20; ++i)
{
std::string str;
int len = dlen(rng);
for (int j=0; j<len; ++j)
str.push_back(s[dchar(rng)]); // push random char
strs.emplace_back(str);
}
// drop list of strins into tree
Tree tree;
for (auto const& str : strs)
{
std::cout << str << '\n';
tree.add_word(str);
}
// now verify every string we just inserted is in the tree
for (auto const& str : strs)
{
if (!tree.has_word(str))
{
std::cerr << "Word \"" << str << "\" should be in tree, but was NOT\n";
std::exit(EXIT_FAILURE);
}
}
std::cout << "All test words found!!\n";
return EXIT_SUCCESS;
}
Output (varies due to random generators)
gctccgga
agtccatt
gagcg
gtggg
tca
aga
cacaggg
cga
tgga
ttatta
cagg
aac
tatttg
gccttat
acctcca
tgagac
aagacg
tgc
aaccgg
tca
All test words found!!
Summary
I strongly advise you run this in the debugger and step through it with a firm grasp on the watch-window. Follow pointer trails to see how things set up as the program progresses. There are many things I did not talk about: proper construction, initialization, Rule of Three compliance etc. I also could have (and would have had this not been an academic case) used smart pointers such as std::unique_ptr<> or std::shared_ptr<>. I sincerely hope you get something out of this. It's only going to get worse from here.
Best of luck
I don't know why but
this :
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
Doesn't look right for me, haven't done c++ for a while and nodes but i don't think that's how you gotta do it? Will check and edit this.
Related
Trying to create a Hash Table with name as key and it's value as the drink, when printTable() is invoked without adding items (i.e. commenting out the following code snippet) it prints out the added image without any address boundary error :
h.addItem("Paul", "Locha");
h.addItem("Kim", "Iced Mocha");
h.addItem("Emma", "Strawberry Smoothy");
h.addItem("Annie", "Hot Chocolate");
h.addItem("Sarah", "Passion Tea");
h.addItem("Pepper", "Caramel Mocha");
h.addItem("Mike", "Chai Tea");
h.addItem("Steve", "Apple Cider");
h.addItem("Bill", "Root Bear");
h.addItem("Marie", "Skinny Latte");
Image of output with given default values
But when items are added either by parsing csv or directly calling the class member function of addItem(), the program renders an error like so :
➜ Hash Table ./hashTableWithStruct.o
fish: Job 1, './hashTableWithStruct.o' terminated by signal SIGSEGV (Address boundary error)
The erroneous code is herewith :
hashTableWithStruct.cpp
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class Hash
{
private:
static const int tableSize = 10;
struct item
{
string name;
string drink;
item *next;
};
item *hashTable[tableSize];
public:
Hash()
{
for (int i = 0; i < tableSize; i++)
{
hashTable[i] = new item;
hashTable[i]->name = "empty";
hashTable[i]->drink = "empty";
hashTable[i]->next = NULL;
}
}
int hashFunct(string key)
{
int hashed = 0;
for (int i = 0; key[i] != '\0'; i++)
{
hashed += (int)key[i];
}
return (hashed % tableSize); //returns the BUCKET value
}
void addItem(string name, string drink)
{
int BUCKET = hashFunct(name);
//if the value at BUCKET hasn't been written yet,
//override the default empty ones
if (hashTable[BUCKET]->name == "empty")
{
hashTable[BUCKET]->name = name;
hashTable[BUCKET]->drink = drink;
}
//otherwise, make a linked list starting from BUCKET
else
{
item *ptr = hashTable[BUCKET];
item *n = new item;
n->name = name;
n->drink = drink;
n->next = NULL;
//the linked list might contain many nodes,
//hence travel to last node and reassign the last node to be this new item
while (ptr != NULL)
ptr = ptr->next;
ptr->next = n;
}
return;
}
int numOfItemsInBucket(int BUCKET)
{
int count = 0;
if (hashTable[BUCKET]->name == "empty")
return count;
else
{
count++;
item *ptr = hashTable[BUCKET];
while (ptr->next != NULL)
{
count++;
ptr = ptr->next;
}
}
return count;
}
void printTable()
{
int num; //holds number of elements(items) in each bucket
for (int i = 0; i < tableSize; i++)
{
num = numOfItemsInBucket(i);
cout << "-----------------------------\n"
<< "Table[" << i << "]" << endl
<< hashTable[i]->name << endl
<< hashTable[i]->drink << endl
<< "# of items in bucket " << i << " : " << num << endl;
cout << "-----------------------------\n";
}
}
};
int main(int argc, char const *argv[])
{
Hash h;
/* string filename = "datasetForHashTable.csv";
ifstream myCSV(filename.c_str());
if (!myCSV.is_open())
{
cout<<"Failed to open file"<<endl;
return 0;
}
string name, drink;
while (myCSV.peek()!=EOF)
{
getline(myCSV, name, ',');
getline(myCSV, drink, '\n');
h.addItem(name, drink);
}
myCSV.close(); */
h.addItem("Paul", "Locha");
h.addItem("Kim", "Iced Mocha");
h.addItem("Emma", "Strawberry Smoothy");
h.addItem("Annie", "Hot Chocolate");
h.addItem("Sarah", "Passion Tea");
h.addItem("Pepper", "Caramel Mocha");
h.addItem("Mike", "Chai Tea");
h.addItem("Steve", "Apple Cider");
h.addItem("Bill", "Root Bear");
h.addItem("Marie", "Skinny Latte");
h.printTable();
return 0;
}
Basically, can't understand why the table won't print with name and drinks, since it works just fine when table is printed without it.
Please help, trying to figure this out since 2 days.
After this while loop
while (ptr != NULL)
ptr = ptr->next;
the pointer ptr is equal to nullptr. So the next statement
ptr->next = n;
invokes undefined behavior due to dereferencing a null pointer.
It seems you mean
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = n;
This program reads a CSV file and enters it into a binary search tree. So far I have managed to insert a new node, put it in order, however, internally, perform a search asking for the Varibale Key but I have not managed to get it to work, could someone help me?
The CSV file is reading a file that includes:
1, name1,123456
2, name2,165151
3, name3,1566516
#include <iostream>
#include <iomanip>
#include <fstream>
#include <memory>
#include <string>
#include <sstream>
#include <vector>
struct Person {
int key;
std::string name;
int num;
};
struct Node : Person {
Node(const Person &person) : Person(person) {}
std::unique_ptr<Node> left, right;
void insert(const Person &person);
};
void Node::insert(const Person &person) {
/* recur down the tree */
if (key > person.key) {
if (left)
left->insert(person);
else
left = std::make_unique<Node>(person);
} else if (key < person.key) {
if (right)
right->insert(person);
else
right = std::make_unique<Node>(person);
}
}
std::vector<Person> persons;
void inorder(Node *root) {
if (root) {
// cout<<"\t";
inorder(root->left.get());
std::cout << '\t' << root->key << ' ' << root->name << ' ' << root->num << '\n';
inorder(root->right.get());
}
}
Node *minValueNode(Node *node) {
Node *current = node;
/* loop down to find the leftmost leaf */
while (current && current->left) current = current->left.get();
return current;
}
int main() {
std::unique_ptr<Node> root;
std::ifstream fin("data.txt");
if (!fin) {
std::cout << "File not open\n";
return 1;
}
std::string line;
const char delim = ',';
while (std::getline(fin, line)) {
std::istringstream ss(line);
Person person;
ss >> person.key;
ss.ignore(10, delim);
std::getline(ss, person.name, delim);
ss >> person.num;
if (ss) persons.push_back(person);
}
for (unsigned int i = 0; i < persons.size(); i++) {
std::cout << std::setw(5) << persons[i].key << std::setw(20)
<< persons[i].name << std::setw(15) << persons[i].num << '\n';
if (!root) root = std::make_unique<Node>(persons[i]);
else root->insert(persons[i]);
}
std::cout << "\n\nInorder:\n";
// cout<<node.name;
inorder(root.get());
return 0;
}
/*bool busqueda(Node *root, int dato)
{
if(root){
return false;
}
else if(root->key==dato){
return true;
}
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
else{
return busqueda(root->right.get(),dato);
}
}*/
Presuming that this is the function you need help with:
bool busqueda(Node *root, int dato) {
if(root){
return false;
}
else if(root->key==dato){
return true;
}
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
else{
return busqueda(root->right.get(),dato);
}
}
There are a few issues here:
if(root) {
This is testing if root is not nullptr, so you're going to immediately bail if root points at something. Meaning, you will immediately return false if any tree is provided, which is the opposite of what you want. Even worse, if root is null you will proceed and try to dereference a null pointer, which will crash the program. Change this line to if (root != nullptr) {.
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
This recursive call looks right except you don't return the result meaning that you will reach the end of a function returning bool without actually returning anything meaningful. Just add return before this function call.
You can mostly express this function using boolean logic as well:
bool busqueda(Node *root, int dato) {
return root != nullptr && (
root->key == dato ||
busqueda((dato < root->key ? root->left : root->right).get(), dato)
);
}
This makes it impossible to forget to return something.
As a side note, enable all compiler warnings -- it would have warned you about reaching the end of a non-void function without returning a value.
Assignment: For this assignment, you are to write a program, which will calculate the results of Reverse Polish expressions that are provided by the user.
You must handle the following situations (errors):
Too many operators (+ - / *)
Too many operands (doubles)
Division by zero
The program will take in a Polish expression that separates the operators and operands by a single space, and terminates the expression with an equals sign.
The program will continue to take and evaluate expressions until the user enters a zero (0) on a line by itself followed by a new line.
Problem 1: I am having a problem with telling the user that there are too many operators and operands. I tried to code it but I have no
idea where to begin with this.
Problem 2: I want the program to end when the user inputs 0, but it is not doing anything when I do it in my program.
#include<iostream>
#include<iomanip>
#include<string>
#include<sstream>
using namespace std;
class Node
{
double data;
Node *top;
Node *ptr;
public:
Node()
{
top = NULL;
ptr = NULL;
}
bool isEmpty()
{
return top == 0;
}
void pushValue(double val)
{
Node *next = new Node;
next->data = val;
next->ptr = top;
top = next;
}
double popVal()
{
if (isEmpty())
{
cout << "Error: Too many operators" << endl;
}
else
{
Node *next = top->ptr;
double ret = top->data;
delete top;
top = next;
return ret;
}
}
//Displays the answer of the equation
void print()
{
cout << "= " << top->data << endl;
}
};
bool isOperator(const string& input)
{
string ops[] = { "+", "-", "*", "/" };
for (int i = 0; i < 4; i++)
{
if (input == ops[i])
{
return true;
}
}
return false;
}
//This function tells the operators what to do with the values.
void performOp(const string& input, Node& stack)
{
double Val1, Val2;
int errorCheck = 0;
Val1 = stack.popVal();
Val2 = stack.popVal();
if (input == "+")
{
stack.pushValue(Val1 + Val2);
}
else if (input == "-")
{
stack.pushValue(Val1 - Val2);
}
else if (input == "*")
{
stack.pushValue(Val1 * Val2);
}
else if (input == "/" && Val2 != 0)
{
stack.pushValue(Val1 / Val2);
}
if (input == "/" && Val2 == 0)
{
cout << "Error: Division by zero" << endl;
errorCheck = 1;
}
if (errorCheck == 0)
{
stack.print();
}
}
int main()
{
cout << "Reverse Polish Notation Calculator!" << endl;
cout << "-------------------------------------------------------------------" << endl;
cout << "Enter your values followed by your operators(Enter 0 to exit)" << endl;
string input;
Node stack;
//Checks the user's input to see which function to use.
while (true)
{
cin >> input;
double num;
if (stringstream(input) >> num)
{
stack.pushValue(num);
}
else if (isOperator(input))
{
performOp(input, stack);
}
else if (input == "0")
{
return 0;
}
}
}
My assignment is to make a binary expression tree to convert postfix expressions to infix expressions in C++. I originally coded all my work in my Xcode IDE and would periodically ssh to linprog4 in terminal to make sure it works there because it has to before I turn it in. I don't understand the errors that are popping up when I compile it using g++ -o proj4.x -std=c++11 proj4_driver.cpp BET.cpp
This assignment was due 2 weeks ago and unfortunately when I was turning in the tar file with all my files, I forgot to include the .tar extension. I don't think that would affect my files but now they aren't compiling and I don't understand the errors I am getting. Could someone skim through my code and see if I am missing anything? I've looked over my code and it doesn't look like I accidentally typed something randomly somewhere.
Here is a screenshot of the errors I am getting
BET.h
#ifndef BET_H
#define BET_H
using namespace std;
class BET {
private:
struct BinaryNode {
string data;
BinaryNode * parent;
BinaryNode * childLeft;
BinaryNode * childRight;
bool visited; // to tell if node has been visited to or not
// Construct a blank copy version BinaryNode when an
// object of BET is created
BinaryNode( const char & d = char{}, BinaryNode * p = NULL,
BinaryNode * l = NULL,
BinaryNode * r = NULL, bool v = false )
: data { d },
parent { p },
childLeft { l },
childRight { r },
visited { v } {}
// Construct a blank move version of BinaryNode when an
// an object of BET is created
BinaryNode( char && d, BinaryNode * p = NULL, BinaryNode * l = NULL,
BinaryNode * r = NULL, bool v = false )
: data { std::move(d) },
parent { p },
childLeft { l },
childRight { r },
visited { v } {}
}; // end of BinaryNode struct
public:
// constructors and destructor
BET();
BET( const string postfix );
BET( const BET & rhs );
~BET();
// help copy constructor
bool buildFromPostfix( const string postfix );
// copy assignment operator
const BET & operator=( const BET & rhs );
void printInfixExpression();
void printPostfixExpression();
size_t size();
size_t leaf_nodes();
bool empty();
bool isOperand(BinaryNode * n);
private:
BinaryNode *root;
size_t leaves, nodes;
bool useP;
void printInfixExpression( BinaryNode * n );
void makeEmpty( BinaryNode* & t );
BinaryNode * clone( BinaryNode * t ) const;
void printPostfixExpression( BinaryNode * n );
size_t size( BinaryNode * t );
size_t leaf_nodes( BinaryNode * t );
}; // end of BET class
#endif
BET.cpp
#include <iostream>
#include <stack>
#include <sstream>
#include <string>
#include "BET.h"
//using namespace std;
// default zero-param constructor
BET::BET() {
root = new BinaryNode;
leaves = 0;
nodes = 0;
}
// one-param constructor, where parameter "postfix" is a
// string containing a postfix expression. The tree should
// be built based on the postfix expression. Tokens in the
// postfix expression are separated by space
BET::BET(const string postfix) {
root = new BinaryNode;
buildFromPostfix(postfix);
}
// copy constructor
BET::BET(const BET & rhs) {
leaves = rhs.leaves;
nodes = rhs.nodes;
root = rhs.root;
}
// destructor
BET::~BET() {
makeEmpty(root);
leaves = nodes = 0;
}
bool BET::buildFromPostfix(const string postfix) {
// Create stack to hold variables
stack<BinaryNode *> s;
stack<BinaryNode> bet;
char token;
string temp;
int index = 1;
bool doubleDigit = false;
int opCount = 0, digitCount = 0;
//stringstream hexToInt;
// iterator through postfix
for (int i = 0; i < postfix.size(); ++i) {
// grab token at iterations index
token = postfix[i];
if ( (token > '0' && token < '9') || (token > 62 && token < 80)) {
// check to see if token is an operand
// create a dynamic object of BinaryNode
BinaryNode *operand = new BinaryNode;
// check to see if next index of postfix is digit
// if its not, then we know its a double digit
// this while loop should only continue as long as the
// next index is between 0 and 9
temp = postfix[i];
while (postfix[i + index] >= '0' && postfix[i + index] <= '9') {
temp += postfix[i + index];
index++;
doubleDigit = true;
}
if (doubleDigit == true) {
i += index;
doubleDigit = false;
index = 1;
operand->data = temp;
} else {
operand->data = postfix[i];
}
s.push(operand);
digitCount++;
} else if (token == '+' || token == '-' || token == '*' || token == '/'){
// check to see if token is operator
BinaryNode *operand = new BinaryNode;
operand->data = postfix[i];
operand->childLeft = s.top();
s.top()->parent = operand;
s.pop();
operand->childRight = s.top();
s.top()->parent = operand;
s.pop();
s.push(operand);
opCount++;
} else {
// if neither, must be space or other character
if (token == ' ') {
} else
return false;
}
}
if (digitCount <= opCount) {
return false;
}
root = s.top();
nodes = size();
//leaves = leaf_nodes();
// THINGS TO DO:
// Make error cases with if statements to return false at some point
return true;
}
// assignment operator
const BET & BET::operator=(const BET & rhs) {
root = clone(rhs.root);
return *this;
}
// public version of printInfixExpression()
// calls the private version of the printInfixExpression fuction
// to print out the infix expression
void BET::printInfixExpression() {
printInfixExpression(root);
}
// public version of printPostfixExpression()
// calls the private version of the printPostfixExpression function
// to print out the postfix expression
void BET::printPostfixExpression() {
printPostfixExpression(root);
}
// public version of size()
// calls the private version of the size function to return
// the number of nodes in the tree
size_t BET::size() {
return size(root);
}
// public version of leaf_nodes()
// calls the private version of leaf_nodes function to return
// the number of leaf nodes in the tree
size_t BET::leaf_nodes() {
return leaf_nodes(root);
}
// public version of empty()
// return true if the tree is empty. return false otherwise
bool BET::empty() {
if (nodes == 0) {
return true;
} else
return false;
}
// checks whether node is operand or not
bool BET::isOperand(BinaryNode * n) {
if (n->data != "+" && n->data != "-" && n->data != "*" && n->data != "/") {
return true;
} else
return false;
}
// private version of printInfixExpression
// print to the standard output the corresponding infix expression
void BET::printInfixExpression(BinaryNode * n) {
//BinaryNode * temp = NULL;
if (n != NULL ) {
printInfixExpression(n->childRight);
cout << n->data << " ";
printInfixExpression(n->childLeft);
}
/*
if (n != NULL && useP == true) {
printInfixExpression(n->childRight);
if (isOperand(n->parent) && n->parent != NULL && !n->childLeft) {
cout << "(";
}
cout << n->data << " ";
if (isOperand(n->parent) && n->parent != NULL && !n->childRight) {
cout << ")";
}
printInfixExpression(n->childLeft);
}
*/
}
// private method makeEmpty()
// delete all nodes in the subtree pointed to by n.
// Called by functions such as the destructor
void BET::makeEmpty(BinaryNode * & n) {
if (n != NULL) {
makeEmpty(n->childLeft);
makeEmpty(n->childRight);
delete n;
}
}
// private method clone()
// clone all nodes in the subtree pointed by n. Called by
// functions such as the assignment operator=
BET::BinaryNode * BET::clone(BinaryNode * n) const {
if (n != NULL) {
root->childRight = clone(n->childRight);
root->childLeft = clone(n->childLeft);
root->data = n->data;
}
return root;
}
// private method printPostfixExpression()
// print to the standard output the corresponding postfix expression
void BET::printPostfixExpression(BinaryNode * n) {
if (n != NULL) {
printPostfixExpression(n->childRight);
printPostfixExpression(n->childLeft);
cout << n->data << " ";
}
}
// private version of size()
// return the number of nodes in the subtree pointed by n
size_t BET::size(BinaryNode * n) {
if (n != NULL) {
size(n->childLeft);
size(n->childRight);
nodes++;
}
return nodes;
}
// return the number of leaf nodes in the subtree pointed by n
size_t BET::leaf_nodes(BinaryNode * n) {
if (n != NULL) {
leaf_nodes(n->childLeft);
leaf_nodes(n->childRight);
if (n->childLeft == NULL && n->childRight == NULL) {
leaves += 1;
}
}
return leaves;
}
Driver.cpp
#include "BET.cpp"
//using namespace std;
int main() {
string postfix;
// get a postfix expression
cout << "Enter the first postfix expression: ";
getline(cin, postfix);
// create a binary expression tree
BET bet1(postfix);
if (!bet1.empty()) {
cout << "Infix expression: ";
bet1.printInfixExpression();
cout << "\n";
cout << "Postfix expression: ";
bet1.printPostfixExpression();
cout << "\nNumber of nodes: ";
cout << bet1.size() << endl;
cout << "Number of leaf nodes: ";
cout << bet1.leaf_nodes() << endl;
// test copy constructor
BET bet2(bet1);
cout << "Testing copy constructor: ";
//bet2.printInfixExpression();
// test assignment operator
BET bet3;
bet3 = bet1;
cout << "Testing assignment operator: ";
//bet3.printInfixExpression();
}
cout << "Enter a postfix expression (or \"quit\" to quit): ";
while (getline(cin, postfix)) {
if (postfix == "quit") {
break;
}
if (bet1.buildFromPostfix(postfix)) {
cout << "Infix expression: ";
bet1.printInfixExpression();
cout << "Postfix expression: ";
bet1.printPostfixExpression();
cout << "Number of nodes: ";
cout << bet1.size() << endl;
cout << "Number of leaf nodes: ";
cout << bet1.leaf_nodes() << endl;
}
cout << "Enter a postfix expression (or \"quit\" to quit): ";
}
return 0;
}
Driver.cpp #include "BET.cpp" should be #include "BET.h"
Or (and this is just for completeness, not recommended), leave the include of the .cpp, but then only try to compile the one .cpp (as in g++ Driver.cpp) - since driver will include BET and so all your code is there and will build.
I am trying to implement a system that would perform something like say the user enters 4 5 +. It would add the 4 and 5 (9) and push 9 into the stack.
For some reason the values in the stack are huge numbers so I believe it has something to do with a pointer or accessing a wrong field but I'm pulling my hair out trying to find the error. Any help on what I'm doing wrong?
#include "stack.h"
int main()
{
stack Test;
bool stop = false;
float runningtotal = 0;
while (stop == false)
{
char input;
cin >> input;
if (input == '+') {
int value1 = Test.top();
Test.pop();
int value2 = Test.top();
Test.pop();
cout << value1+value2 << endl;
Test.push(value1 + value2);
}
cout << Test.top();
std::getchar();
std::getchar();
}
And the implementation of stack
#include "stack.h"
stack::stack()
{
maxsize = MaxSize;
currentsize = 0;
sptr = new StackElement[maxsize];
}
stack::~stack()
{
delete [] sptr;
}
void stack::push(StackElement data)
{
if (currentsize < maxsize)
{
sptr[currentsize] = data;
currentsize++;
} else {
cout << "Stack is full ;-;";
}
}
void stack::pop()
{
if (currentsize == 0) {
cout << "Empty stack? ;-;";
return;
}
currentsize--;
}
StackElement stack::top()
{
if (currentsize == 0) {
cout << "Empty stack u ninja ;-;";
return NULL;
} else {
return (sptr[currentsize]);
}
}
void stack::push(StackElement data)
{
if (currentsize < maxsize)
{
sptr[currentsize] = data;
currentsize++; //<--- incrementing after so nothing in [currentsize] now
} else {
cout << "Stack is full ;-;";
}
}
StackElement stack::top()
{
if (currentsize == 0) {
cout << "Empty stack u ninja ;-;";
return NULL;
} else {
return (sptr[currentsize]);// should use currentsize-1
// latest filled cell
// since its pushing from top
}
}
Be sure to convert those ascii codes(49 ish) from keyboard to integer type explanations.
input - 48 should do it.