C Creating a binary tree based on a sequence - c++

I need help adjusting the createTree function.
Which accepts a string and after that character by character traverses it, creating a binary tree based on it
If it encounters the character 0, it recursively creates two sub-branches.
If it encounters another character, it saves it in the leaf node.
For the string in the example, I need to make a tree as in the picture, but the function does not work properly for me. Thank you in advance for your advice.
int x = 0;
Node* createTree(string str, int si, int ei)
{
if (si > ei)
return NULL;
Node *root = new Node((str[si] - '0'));
if(str[si] != '0')
{
x++;
root->m_Data = (str[si] - '0');
return root;
}
if(str[si]=='0')
{
x++;
root->m_Left = createTree(str,x,ei);
root->m_Right = createTree(str,x,ei);
}
return root;
}
int main ()
{
string str = "050067089";
Node *node = createTree(str,0,str.length());
printPreorder(node);
return 0;
}

The problem can quite easily be broken down into small steps (what you partly did in your question).
Start iterating at the first character
Create the root node
If the current character is non-zero, set the value of this node to this character
If current character is a zero, set this node to zero, create a left and a right node and get back to step 3 for every one of them. (That's the recursive part.)
Below is my implementation of this algorithm.
First, a little bit of setting up:
#include <iostream>
#include <string>
#include <memory>
struct Node;
// Iterator to a constant character, NOT a constant iterator
using StrConstIt = std::string::const_iterator;
using UniqueNode = std::unique_ptr<Node>;
struct Node
{
int value;
UniqueNode p_left;
UniqueNode p_right;
Node(int value)
: value(value) {}
Node(int value, UniqueNode p_left, UniqueNode p_right)
: value(value), p_left(std::move(p_left)), p_right(std::move(p_right)) {}
};
As you can see, I'm using std::unique_ptr for managing memory. This way, you don't have to worry about manually deallocating memory. Using smart pointers is often considered the more "modern" approach, and they should virtually always be preferred over raw pointers.
UniqueNode p_createNodeAndUpdateIterator(StrConstIt& it, StrConstIt stringEnd)
{
if (it >= stringEnd)
return nullptr;
UniqueNode node;
if (*it == '0')
// Create node with appropriate value
// Create branches and increment iterator
node = std::make_unique<Node>(
0,
p_createNodeAndUpdateIterator(++it, stringEnd),
p_createNodeAndUpdateIterator(it, stringEnd)
);
else
{
// Create leaf node with appropriate value
node = std::make_unique<Node>(*it - '0');
// Increment iterator
++it;
}
return node;
}
UniqueNode p_createTree(StrConstIt begin, StrConstIt end)
{
return p_createNodeAndUpdateIterator(begin, end);
}
The first function takes a reference to the iterator to the next character it should process. That is because you can't know how much characters a branch will have in its leaf nodes beforehand. Therefore, as the function's name suggests, it will update the iterator with the processing of each character.
I'm using iterators instead of a string and indices. They are clearer and easier to work with in my opinion — changing it back should be fairly easy anyway.
The second function is basically syntactic sugar: it is just there so that you don't have to pass an lvalue as the first argument.
You can then just call p_createTree with:
int main()
{
std::string str = "050067089";
UniqueNode p_root = p_createTree(str.begin(), str.end());
return 0;
}
I also wrote a function to print out the tree's nodes for debugging:
void printTree(const UniqueNode& p_root, int indentation = 0)
{
// Print the value of the node
for (int i(0); i < indentation; ++i)
std::cout << "| ";
std::cout << p_root->value << '\n';
// Do nothing more in case of a leaf node
if (!p_root->p_left.get() && !p_root->p_right.get())
;
// Otherwise, print a blank line for empty children
else
{
if (p_root->p_left.get())
printTree(p_root->p_left, indentation + 1);
else
std::cout << '\n';
if (p_root->p_right.get())
printTree(p_root->p_right, indentation + 1);
else
std::cout << '\n';
}
}

Assuming that the code which is not included in your question is correct, there is only one issue that could pose a problem if more than one tree is built. The problem is that x is a global variable which your functions change as a side-effect. But if that x is not reset before creating another tree, things will go wrong.
It is better to make x a local variable, and pass it by reference.
A minor thing: don't use NULL but nullptr.
Below your code with that change and the class definition included. I also include a printSideways function, which makes it easier to see that the tree has the expected shape:
#include <iostream>
using namespace std;
class Node {
public:
int m_Data;
Node* m_Left = nullptr;
Node* m_Right = nullptr;
Node(int v) : m_Data(v) {}
};
// Instead of si, accept x by reference:
Node* createTree(string str, int &x, int ei)
{
if (x >= ei)
return nullptr;
Node *root = new Node((str[x] - '0'));
if(str[x] != '0')
{
root->m_Data = (str[x] - '0');
x++;
return root;
}
if(str[x]=='0')
{
x++;
root->m_Left = createTree(str,x,ei);
root->m_Right = createTree(str,x,ei);
}
return root;
}
// Overload with a wrapper that defines x
Node* createTree(string str)
{
int x = 0;
return createTree(str, x, str.length());
}
// Utility function to visualise the tree with the root at the left
void printSideways(Node *node, string tab) {
if (node == nullptr) return;
printSideways(node->m_Right, tab + " ");
cout << tab << node->m_Data << "\n";
printSideways(node->m_Left, tab + " ");
}
// Wrapper for above function
void printSideways(Node *node) {
printSideways(node, "");
}
int main ()
{
string str = "050067089";
Node *node = createTree(str);
printSideways(node);
return 0;
}
So, as you see, nothing much was altered. Just si was replaced with x, which is passed around by reference, and x is defined locally in a wrapper function.
Here is the output:
9
0
8
0
7
0
6
0
5

Related

search function causes program to crash

I have been going through the debugger but can't seem to pinpoint exactly what is going wrong. I have come to my own conclusion i must be missing a nullptr check somewhere or something. If anyone can provide some help it would be greatly appreciated.
error message from debugger
error msg
which looks like makes the program crash on this line:
if (node->children_[index] == nullptr) {
search function
Node* search(const string& word, Node* node, int index) const {
Node* temp;
//same as recurssive lookup just difference is returns node weather terminal or not
if (index < word.length()) {
index = node->getIndex(word[index]);
if (node->children_[index] == nullptr) {
return nullptr;
}
else {
temp = search(word, node->children_[index], index++);
}
}
return temp; // this would give you ending node of partialWord
}
Node struct for reference
struct Node {
bool isTerminal_;
char ch_;
Node* children_[26];
Node(char c = '\0') {
isTerminal_ = false;
ch_ = c;
for (int i = 0; i < 26; i++) {
children_[i] = nullptr;
}
}
//given lower case alphabetic charachters ch, returns
//the associated index 'a' --> 0, 'b' --> 1...'z' --> 25
int getIndex(char ch) {
return ch - 'a';
}
};
Node* root_;
int suggest(const string& partialWord, string suggestions[]) const {
Node* temp;
temp = search(partialWord, root_, 0);
int count = 0;
suggest(partialWord, temp, suggestions, count);
return count;
}
Might be a very simple thing. Without digging I am not sure about the rank of the -> operator versus the == operator. I would take a second and try putting parenthesis around the "node->children_[index] == nullptr" part like this:
(node->children_[index]) == nullptr
just to make sure that the logic runs like you seem to intend.
Dr t
I believe the root cause is that you're using index for two distinct purposes: as an index into the word you're looking for, and as an index into the node's children.
When you get to the recursion, index has changed meaning, and it's all downhill from there.
You're also passing index++ to the recursion, but the value of index++ is the value it had before the increment.
You should pass index + 1.
[An issue in a different program would be that the order of evaluation of function parameters is unspecified, and you should never both modify a variable and use it in the same parameter list. (I would go so far as to say that you should never modify anything in a parameter list, but many disagree.)
But you shouldn't use the same variable here at all, so...]
I would personally restructure the code a little, something like this:
Node* search(const string& word, Node* node, int index) const {
// Return immediately on failure.
if (index >= word.length())
{
return nullptr;
}
int child_index = node->getIndex(word[index]);
// The two interesting cases: we either have this child or we don't.
if (node->children_[child_index] == nullptr) {
return nullptr;
}
else {
return search(word, node->children_[child_index], index + 1);
}
}
(Side note: returning a pointer to a non-const internal Node from a const function is questionable.)

Recursive method to find value in linked list

template<class Type>
int StringList<Type>::find(Type value)
{
int count = 0;
// Start of linked list
Node<Type> *current = head;
// Traverse list until end (NULL)
while (current != NULL)
{
// Increase counter if found
if (current->data == value)
{
count++;
}
// If not, move to the next node
current = current->next;
}
cout << value << " was found " << count << " times" << endl;
return 0;
// same function but using Recursive method
// Start of linked list
Node<Type> *current = head;
int count = 0;
// Thinking this is the base case, since its similar to the while loop
if (current == NULL)
{
return 0;
}
// same as the while loop, finding the value increase the count, or in this case just prints to console
if ((current->data == value))
{
cout << "Found match" << endl;
return 0;
}
else
{ // If it didnt find a match, move the list forward and call the function again
current = current->next;
return find(value);
}
}
the function is supposed to find the value searched and return how many times that certain value was in the linked list.
how can I turn the first method, which uses a while loop, into something that does the same thing but uses recursion?
For starters instead of the return type int it is better to use an unsigned type like for example size_t
You can use the following approach. Define two methods. The first one is a public non-static method find defined like
template<class Type>
size_t StringList<Type>::find( const Type &value ) const
{
return find( head, value );
}
The second one is a private static method with two parameters defined like
template<class Type>
static size_t StringList<Type>::find( Node<Type> *current, const Type &value )
{
return current == nullptr ? 0 : ( current->data == value ) + find( current->next, value );
}
In order to use recursion, you will need to change the signature of your find function (or add a new function with the different signature) to take a node pointer as a parameter:
template<class Type>
int StringList<Type>::find(Type value, Node<Type> *where)
{
if (where != nullptr)
{
// Do things
}
}
Then, when you traverse the list, you pass where->next to the function. Once you hit the end of the list, with a nullptr value, the stack unrolls.
A key aspect of recursion as that the function or method being used only has to process a single node of your container. It then calls itself with the next node to be processed until there are no more nodes. In order to make this work, that function needs the node to process as a parameter, which is where your current code runs into problems.
Keep in mind that the elegance and simplicity of recursion is not free. Every call that a method makes to itself eats up stack, so a sufficiently large container can result in a crash if the stack for your process is depleted.
how can I turn the first method, which uses a while loop, into
something that does the same thing but uses recursion?
The following would be closer to what you want. You really should provide an [MCVE] ... the lack of which forces many guesses and assumptions about your code.
// It looks like StringList is a class (I ignored template issues),
// and it appears that your class holds 'anchors' such as head
// StringList is probably the public interface.
//
// To find and count a targetValue, the code starts
// at the head node, and recurses through the node list.
// I would make the following a public method.
//
int StringList::findAndCountTargetValue(int targetValue)
{
int retVal = 0;
if (nullptr != head) // exists elements to search?
retVal = head->countTV(targetValue); // recurse the nodes
// else no match is possible
return(retVal);
}
// visit each node in the list
int Node::countTV(const int targetValue)
{
int retVal = 0; // init the count
if (data != targetValue) // no match
{
if(nullptr != next) // more elements?
retVal += next->countTV() // continue recursive count
}
else
{
std::cout << "Found match" << std::endl; // for diag only
retVal += 1; // because 1 match has been found
if(nullptr != next) // more elments
retVal += next->countTV(); // continue recursive count
}
return (retVal); // always return value from each level
}

c++ linked list storing strings

I am creating a custom linked list class to store strings from a program I created for an assignment. We were given a linked list handout that works for ints and were told to retool it for string storage, however I am running into an error when trying to run it.
I'm getting the error ""terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid"" (which I searched around and found it was because of a string being set to null, however I do not know how to fix the error, I'm guessing it is with line 8 but I've toyed around with it to no success.) I've searched around and looked through the similar questions but could not find anything that helped.
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstdio>
#include <iomanip>
using namespace std;
struct node {
node(string current) { data=current; next=NULL; }
string data;
node *next;
};
class list {
public:
list(int N=0, string current);
~list();
bool empty() const { return N == 0; }
void clear();
void insert(int, const string &);
void push_front(const string &current);
friend ostream & operator<<(ostream &out, const list &current);
private:
int N;
node *head;
node *findnode(int);
};
list::list(int M, string current) {
N = M;
head = new node;
for (int i=0; i<N; i++)
insert(0, current);
}
list::~list() {
clear();
delete head;
}
void list::clear() {
while (!empty()) remove(0);
}
void list::insert(int i, const string &din) {
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
inline
node *list::findnode(int i) {
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
void list::push_front(const string &current) {
head = new node;
head->next;
}
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
const string rank[] = { "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King" };
const string suit[] = { "Clubs", "Diamonds", "Hearts", "Spades" };
string random_card(bool verbose=false) {
string card;
card = rank[ rand()%13 ];
card += " of ";
card += suit[ rand()%4 ];
if (verbose)
cout << card << "\n";
return card;
}
int main(int argc, char *argv[])
{
bool verbose = false;
int seedvalue = 0;
string stop_card = "Queen of Hearts";
for (int i=1; i<argc; i++) {
string option = argv[i];
if (option.compare(0,6,"-seed=") == 0) {
seedvalue = atoi(&argv[i][6]);
} else if (option.compare(0,6,"-stop=") == 0) {
stop_card = &argv[i][6];
} else if (option.compare("-verbose") == 0) {
verbose = true;
} else
cout << "option " << argv[i] << " ignored\n";
}
srand(seedvalue);
list deck[4];
while (1) {
string card = random_card(verbose);
char first[10];
char second[10];
sscanf(card.c_str(), "%s of %s", first,second);
// reverse engineer card suit and rank
int index2;
//suit index
for(int i=0; i<4; i++){
if(suit[i]==second){
index2=i;
break;
}
}
deck[index2].push_front(first);
if (card.compare(stop_card)==0){
break;
}
}
// print formatted table contents to stdout
cout << "Clubs : ";
cout << setw(3) << deck[0];
cout << endl;
cout << "Diamonds : ";
cout << setw(3) << deck[1];
cout << endl;
cout << "Hearts : ";
cout << setw(3) << deck[2];
cout << endl;
cout << "Spades : ";
cout << setw(3) << deck[3];
cout << endl;
}
The following are significant problems that will either hinder building (read: compile-time bugs) or actual runtime. This makes no claim these are all the bugs, but its certainly worth considering. I should note right off the top that the concept of a "sentinel" head-node allocation is almost- never needed in linked list management, and this code is not one of the exceptions. If the list is "empty" head should be null. If it isn't empty, head should not be null. Its just that simple, and this code would be leaps-and-bounds simpler if that were followed.
With that, read on.
Invalid Code:
list(int N=0, string current);
Reason: C++ requires all arguments following the first argument that is provided a default value to also have default values. This would be valid if N was the second parameter, or if current was also given a default value (or of course ,if neither had default values). All of the following are valid:
list(int N, string current);
list(int N, string current = "");
list(int N=0, string current = "");
As-written, it will fail to compile.
Invalid code: No matching constructor available
head = new node;
Reason: The structure node does not defined a default-compliant constructor (one that either has no parameters, or all parameters with default value provisions) but does specify a non-default constructor (one that requires at least one parameter). As a result, the language-supplied default constructor is not auto-generated and there is no node::node() constructor to be found.
Incorrect Code: Expression result is unused
void list::push_front(const string &current) {
head = new node;
head->next; // THIS LINE
}
Reason: This code blindly overwrites whatever is currently occupied in the head pointer with a new (invalid, see above for why) node allocation. Anything that was in head prior is leaked forever, and current is unused whatsoever. Fix this by allocating a new node with current as the value, settings its next pointer to head and head to the new node:
void list::push_front(const string &current)
{
node *p = new node(current);
p->next = head;
head = p;
}
Infinite Recursion
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
Reason: This code literally invokes itself. Recursively. Forever (well, until you run out of call-stack space).
NULL Pointer Dereference
inline node *list::findnode(int i)
{
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
Reason: This will walk the list uninhibited by validity checking for i iterations. Now imagine what this does on an empty list (in your case, that means head is non-null, but head->next is null) when passed anything besides -1: It will return NULL for i=0 and is outright undefined behavior for everything else.
NULL Pointer Dereference
void list::insert(int i, const string &din)
{
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
This assumes pp will never be null on return, and as we already discussed with the prior item, it most certainly can be when head is the sole node in your list, and is therefore "empty". This makes no attempt at checking pp for NULL prior to using it for dereferencing. This kid-gloves handling and the exceptions that have to be accounted for are directly related to maintaining a "sentinel" head node. The simplest way to fix it is to (a) Don't use sentinel nodes; use the universal sentinel value nullptr, and (b) check your return values before using them.
Ambiguous Reference: rank
card = rank[ rand()%13 ];
Reason: The standard library defines a special struct called std::rank used for determining the number of dimensions in a multi-dimension array. With the using namespace std; at the top of your code, the compiler is now forced to choose which one (the one in namespace std or the array you've defined prior to this code), and it cannot do so unequivocally. Thus it will not compile. Note: this is brought in by implicitly including <type_traits>, which is likely included by <string>, <iostream>, <iomanip> or any of a number of other nested includes. You can solve it a number of ways, including (but not limited to) a creative using clause, renaming the rank array to something that doesn't conflict, using a functional wrapper around a local static rank in the function etc.
Implicit conversion from signed to unsigned type (minor)
srand(seedvalue);
Reason: std::srand() takes an unsigned int parameter; you're passing a signed integer. Either static-cast to unsigned int or change the type of seedValue to unsigned int.
Invalid Code
list deck[4];
Reason: Class list does not have a default constructor. Recall the first item in this response. If you fix that, you will fix this as well.
And I didn't even run the code yet. I would strongly advise working on these issues, and give serious consideration to not using a "sentinel" node for your list head. Linked list code practically writes itself once you "know" a null head means the list is empty, a non-null head means it isn't.
I make no claims this is all the bugs. These were just ones I saw while reviewing the code, and all but one of them is significant.
EDIT Sample operator overload
Note: If you fix your linked list to use null as a head value when the list is empty (advised) this will need to change to simply start at head rather than head>next.
std::ostream& operator <<(std::ostream& os, const list& lst)
{
const node *p = lst.head ? lst.head->next : nullptr;
while (p)
{
os << p->data;
if ((p = p->next)) // note: assignment intentional
os << ',';
}
return os;
}

C++ vector and segmentation faults

I am working on a simple mathematical parser. Something that just reads number = 1 + 2;
I have a vector containing these tokens. They store a type and string value of the character. I am trying to step through the vector to build an AST of these tokens, and I keep getting segmentation faults, even when I am under the impression my code should prevent this from happening.
Here is the bit of code that builds the AST:
struct ASTGen
{
const vector<Token> &Tokens;
unsigned int size,
pointer;
ASTGen(const vector<Token> &t) : Tokens(t), pointer(0)
{
size = Tokens.size() - 1;
}
unsigned int next()
{
return pointer + 1;
}
Node* Statement()
{
if(next() <= size)
{
switch(Tokens[next()].type)
{
case EQUALS
:
Node* n = Assignment_Expr();
return n;
}
}
advance();
}
void advance()
{
if(next() <= size) ++pointer;
}
Node* Assignment_Expr()
{
Node* lnode = new Node(Tokens[pointer], NULL, NULL);
advance();
Node* n = new Node(Tokens[pointer], lnode, Expression());
return n;
}
Node* Expression()
{
if(next() <= size)
{
advance();
if(Tokens[next()].type == SEMICOLON)
{
Node* n = new Node(Tokens[pointer], NULL, NULL);
return n;
}
if(Tokens[next()].type == PLUS)
{
Node* lnode = new Node(Tokens[pointer], NULL, NULL);
advance();
Node* n = new Node(Tokens[pointer], lnode, Expression());
return n;
}
}
}
};
...
ASTGen AST(Tokens);
Node* Tree = AST.Statement();
cout << Tree->Right->Data.svalue << endl;
I can access Tree->Data.svalue and get the = Node's token info, so I know that node is getting spawned, and I can also get Tree->Left->Data.svalue and get the variable to the left of the =
I have re-written it many times trying out different methods for stepping through the vector, but I always get a segmentation fault when I try to access the = right node (which should be the + node)
Any help would be greatly appreciated.
There's plenty more code that we haven't seen, so I can't tell you precisely what's going on, but I see a few things that are reasons for concern. One is that the Statement() method doesn't always return a value. If the first if test doesn't pass, then we call advance() and fall off the bottom of the routine without an explicit return. The caller will try to get the return value of the function but it'll get garbage. This could lead to all sorts of problems, including things like double free() calls, etc, which can easily cause segfaults.
Expression() has the same problem.

Nightmare Expression Tree with over-constrained class

I inadvertently let my students overconstrain a shared class used to solve the following problem. I realized it might be a problem denizens of this site might enjoy.
The first team/function, getNodes, takes a string representing a prefix expression using signed integers and the four operations +, -, *, and / and produces the corresponding null terminated linked list of tokens, using the class Node, with tokens linked through the "right" pointer.
The second team/function, getTree, takes a similar string, passes it to getNodes, and relinks the resultant nodes to be an expression tree.
The third team/function, evaluate, takes a similar string, passes it to getTree, and evaluates the resultant expression tree to form an answer.
The over-constrained exptree.h follows. The problem has to be solved by writing just the three functions defined above, no additional functions.
#ifndef EXPTREE_H_
#define EXPTREE_H_
using namespace std;
enum Ops{ADD, SUB, MUL, DIV, NUM};
class Node {
private:
int num;
Ops op;
Node *left, *right;
public:
friend Node *getNodes(string d);
friend Node *getTree(string d);
friend int evaluate (string);
};
int evaluate(string d);
Node *getNodes(string d);
Node *getTree(string d);
#endif
The only libraries that can be used are these
#include <iostream>
#include <vector>
#include <string>
#include "exptree.h"
For those of you worried about my students, I will be pointing out today how just a couple of more well placed functions would allow this problem to be easily solved. I know the expression tree can code rational numbers and not just integers. I'll be pointing that out today as well.
Here is the driver program I gave them based on their specs.
#include <iostream>
#include <string>
#include "exptree.h"
using namespace std;
void test(string s, int target) {
int result = evaluate(s);
if (result == target)
cout << s << " correctly evaluates to " << target << endl;
else
cout << s << "(" << result
<< ") incorrectly evaluates to " << target << endl;
}
int main() {
test("42", 42);
test("* - / 4 2 1 42", 42);
test("* - / -4 +2 -1 2", -2);
test("* - / -4 +2 -1 2 ", -2);
test("* 9 6", 54);
return 0;
}
Can you write the three functions in as elegant a fashion as possible to solve this nightmarish problem?
The getNodes and getTree functions would be pretty trivial to write under these constraints, so I just skipped ahead to the interesting part. You would naturally evaluate an expression tree recursively, but that is not an option here because the eval function only takes a string. Sure, you could restringify the remaining tree into a prefix expression and call eval recursively on that, but that would just be stupid.
First, I convert the expression tree into a postfix expression, using an explicit stack as the poor man's recursion. Then I evaluate that with the standard operand stack.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
#include "exptree.h"
int evaluate(string d){
Node* tree = getTree(d);
//convert tree to postfix for simpler evaluation
vector<Node*> node_stack;
node_stack.push_back(tree);
Node postfix_head;
Node* postfix_tail = &postfix_head;
while(node_stack.size() > 0){
Node* place = node_stack.back();
if(place->left == 0){
if(place->right == 0){
postfix_tail->right = place;
node_stack.pop_back();
} else {
node_stack.push_back(place->right);
place->right = 0;
}
} else {
node_stack.push_back(place->left);
place->left = 0;
}
}
//evaluate postfix
Node* place = postfix_head.right;
vector<int> stack;
while(place != 0){
if(place->op != NUM){
int operand_a, operand_b;
operand_b = stack.back();
stack.pop_back();
operand_a = stack.back();
stack.pop_back();
switch(place->op){
case ADD:
stack.push_back(operand_a + operand_b);
break;
case SUB:
stack.push_back(operand_a - operand_b);
break;
case MUL:
stack.push_back(operand_a * operand_b);
break;
case DIV:
stack.push_back(operand_a / operand_b);
break;
}
} else {
stack.push_back(place->num);
}
place = place->right;
}
return stack.back();
}
I think that "no additional functions" is a too tough requirement. The easiest way to implement e.g. getTree is probably recursive, and it requires defining an additional function.
Node* relink(Node* start) // builds a tree; returns the following node
{
if (start->op == NUM)
{
Node* result = start->right;
start->left = start->right = NULL;
return result;
}
else
{
start->left = start->right;
start->right = relink(start->left);
return relink(start->right);
}
}
Node* getTree(string d)
{
Node* head = getNodes(d);
relink(head);
return head;
}
I could implement recursion by using an explicit stack (implemented by std::vector) but that is ugly and obscure (unless you want you students to practice exactly that).
For what its worth, here is the solution I coded up just before I posted the question
#include <iostream>
#include <vector>
#include "exptree.h"
using namespace std;
Node *getNodes(string s) {
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *list;
int sign, num;
s += " "; // this simplifies a lot of logic, allows trailing white space to always close off an integer
list = (Node *) (num = sign = 0);
for (int i=0; i<s.size(); ++i) {
char c = s[i]; // more efficient and cleaner reference to the current character under scrutiny
if (isdigit(c)) {
if (sign == 0) sign = 1; // if sign not set, then set it. A blank with a sign==0 now signifies a blank that can be skipped
num = 10*num + c - '0';
} else if (((c=='+') || (c=='-')) && isdigit(s[i+1])) { // another advantage of adding blank to string above so don't need a special case
sign = (c=='+') ? 1 : -1;
} else if ( !isspace(c) && (c != '+') && (c != '-') && (c != '*') && (c != '/')) {
cout << "unexpected character " << c << endl;
exit(1);
} else if (!isspace(c) || (sign != 0)) { // have enough info to create next Node
list->left = (list == 0) ? (list = new Node) : (list->left->right = new Node); // make sure left pointer of first Node points to last Node
list->left->right = 0; // make sure list is still null terminated
list->left->op = (c=='+' ? ADD : (c=='-' ? SUB : (c=='*' ? MUL : (c=='/' ? DIV : NUM)))); // choose right enumerated type
list->left->num = (list->left->op==NUM) ? sign*num : MININT; // if interior node mark number for evaluate function
num = sign = 0; // prepare for next Node
}
}
return list;
}
Node *getTree(string s) {
Node *nodes = getNodes(s), *tree=0, *root, *node;
vector<Node *> stack;
if (nodes == 0) return tree;
root = tree = nodes;
nodes = nodes->right;
for (node=nodes; node != 0; node=nodes) {
nodes = nodes->right;
if (root->op != NUM) { // push interior operator Node on stack til time to point to its right tree
stack.push_back(root);
root = (root->left = node); // set interior operator Node's left tree and prepare to process that left tree
} else {
root->left = root->right = 0; // got a leaf number Node so finish it off
if (stack.size() == 0) break;
root = stack.back(); // now pop operator Node off the stack
stack.pop_back();
root = (root->right = node); // set its left tree and prepare to process that left tree
}
}
if ((stack.size() != 0) || (nodes != 0)) {
cout << "prefix expression has missing or extra terms" << endl;
exit(1);
}
return tree;
}
int evaluate(string s) {
// MININT is reserved value signifying operator waiting for a left side value, low inpact since at edge of representable integers
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *tree = getTree(s);
vector<Node *> stack;
int v = 0; // this is value of a leaf node (a number) or the result of evaluating an interior node
if (tree == 0) return v;
do {
v = tree->num;
if (tree->op != NUM) {
stack.push_back(tree);
tree = tree->left; // prepare to process the left subtree
} else while (stack.size() != 0) { // this while loop zooms us up the right side as far as we can go (till we come up left side or are done)
delete tree; // done with leaf node or an interior node we just finished evaluating
tree = stack.back(); // get last interior node from stack
if (tree->num == MININT) { // means returning up left side of node, so save result for later
tree->num = v;
tree = tree->right; // prepare to evaluate the right subtree
break; // leave the "else while" for the outer "do while" which handles evaluating an expression tree
} else { // coming up right side of an interior node (time to calculate)
stack.pop_back(); // all done with interior node
v = tree->op==ADD ? tree->num+v : (tree->op==SUB ? tree->num-v : (tree->op==MUL ? tree->num*v : tree->num/v)) ;
}
}
} while (stack.size() != 0);
return v;
}