Data Structures - Lab 11
Tuesday, November 3rd
Constructing an Expression/Parse Tree
Note: Several of the graphics in this lab were taken from "Data Structures and Algorithm Analysis in C" by Mark Allen Weiss
This week's lab will be another challenging one. You will really need to THINK about the problem and use your planning and debugging skills to get things working properly.
Yesterday at the start of class I had you consider a parse tree that looked like this:

In infix notation, this represents the expression :
( 9 + 5 * 3 ) / ( 8 - 4)
But in postfix notation (review section 14.3 for a review of postfix notation) we would write this as:
9 5 3 * + 8 4 - /
Today we want to look at how you could take this postfix notation expression and build the parse tree shown.
Before you start
To begin download and unzip the following file:
Yesterday in class we spent some time creating one implementation of a BinaryTree. I told you in class that this was relatively easy to write, but in fact suffered some problems and was harder to debug in the long run. Your author creates an implementation that uses both code based recursion and object recursion to define both standard BinaryTree and an EmptyBinaryTree. Take ten minutes to look at the code in the file binarytree.py and make sure you understand what is happening. For the most part it isn't complicated, but you may be confused at first by the use of the object recursion (the use of the EmptyBinaryTree class). If you have questions PLEASE ask for help.
Part A - Building a Parse Tree
For this part of the lab you must "fill-in-the-blank" for the method named buildParseTree() contained in lab11.py
This method takes in a String which is assumed to be a well-formatted, space separated, postfix notation expression. For example, suppose the input is
"a b + c d e + * *"
This method should take this String and process it to the appropriate parse tree. For the example above, this looks like:

In order to do this, the method does the following:
1) Split the string up into it's individual tokens (numbers and operators)
2) Create an empty Stack
3) Work through the tokens from front to back. For each token, the method should create a Binary Tree with the token value as the rootValue of the tree. It should then process this BinaryTree using the following rules:
a) If the root/token is a number (not an operator) then push the tree/token on the stack. Notice that these trees will retain the values of "None" for their children. They will become leaf nodes.
b) If the root/token is an operator it should pop the last two Binary Trees off the Stack, set these as the Left and Right Children of the current Binary Tree, and then push this new Tree back on the stack.
This overall process would look something like this. Again, suppose the input was "a b + c d e + * *"
The first token is the number "a" (ok, a isn't a number, but it IS a non-operator) so a tree is made and pushed on the stack. Again, the children of this tree are not set, which means that this tree represents a leaf.
The second token is the number "b" so a tree is made and pushed on the stack.

Next, a "+" is read, so first a tree is created with "+" as it's root value. Then, the last two items (trees) on the stack are popped off and the left and right child of this new tree are set to point to these children (pay attention to the order in which they were encountered and popped off so you set the correct one to the correct child). Finally, this new tree is pushed back on the stack.

Next, c, d, and e (all numbers/non-operators) are read. For each, a one-node tree with no children (a leaf) is created and pushed onto the stack.

Now a "+" is read, so a new node is made with this as the root and the top two items on the stack (d and e) are popped off and set to it's children. Finally, it is pushed onto the stack

Next, a "*" is read, so a new node is made with this as the root and the top two items on the stack (at this point, c, and the node whose root value is "+" with children of d and e) are popped off and set to it's children. Again, this new tree is pushed onto the stack

Finally, the last symbol (the second "*") is read. It is put into a new tree whose children are set to the top two items on the stack and the final tree pushed back onto the stack.

If everything went correctly, and the input really was a well balanced postfix notation expression, than the stack should end with only one item on it - the fully formed parseTree. This tree should be popped and returned to the user for use in testing.
Your job - write the code described above.
When you have done it you should be able to test it by invoking the following:
>>> myTree = buildParseTree("1 2 + 3 4 5 + * *")
>>> evaluate(myTree)
81
>>> myTree = buildParseTree("9 5 3 * + 8 4 - /")
>>> evaluate(myTree)
6
Notice that the evaluate() expression is already done for you and is part of lab11.py
Part B - Printing the expression represented by the parse tree
It would be nice to be able to print the expression represented by a parse tree so that we can see if it looks right or not. Unfortunately, most of us aren't used to reading postfix notation. But we are able to read infix notation.
Complete the toInfix() method contained in lab11.py.
This method should
In order to do this you have to consider what it means to do this. Consider the following SIMPLE parse tree generated by running "buildParseTree("1 2 +")

If I take this tree and send it as a parameter to toInfix() I would expect that this prints:
1 + 2
In other words, first, it prints the left child, then it prints the operator, then it prints the right child.
Of course this is made more complicated by larger trees. We must recursively descend through the tree to print out the sub trees. For example,

would return the string
a + b * c * d + e
ACTUALLY, due to order of operations and precedence of operations we would WANT to print
( a + b ) * c * ( d + e )
You will get full credit for this lab if you can return the first of these even though it is incorrect (because my goal is to see if you can do the recursive walk of the tree).
You will earn a bonus point if you figure out how to print the second, CORRECT, representation (it turns out to be fairly easy if you perform a small "lookahead" before you start printing a given tree).
When you have done it you should be able to test it by invoking the following:
>>> myTree = buildParseTree("1 2 + 3 4 5 + * *")
>>> out = toInfix(myTree)
>>> print out
1+2*3*4+5
>>> myTree = buildParseTree("9 5 3 * + 8 4 - /")
>>> out = toInfix(myTree)
>>> print out
9+5*3/8-4
Please note that the answers shown above are the "incorrect" but acceptable versions. I strongly encourage you to attempt to get the fully correct and "bonus point" versions...
>>> myTree = buildParseTree("1 2 + 3 4 5 + * *")
>>> out = toInfix(myTree)
>>> print out
(1+2)*3*(4+5)
>>> myTree = buildParseTree("9 5 3 * + 8 4 - /")
>>> out = toInfix(myTree)
>>> print out
(9+5*3)/(8-4)
NOTE : While you may use the preorder, inorder, postorder methods as a reference point in the completion of this activity, you should not use calls to any of those methods during the completion of this part of the assignment.
Submitting your Assignment
To get credit for this weeks lab you must upload lab11.py
Recall that all materials are due by the start of class on Wednesday.