CS 31600 - Homework Assignment 2

This assignment is about expression trees, which are binary trees that represent arithmetic expressions. In this assignment you will see how variations on preorder, inorder, and postorder traversals can do useful work on expression trees. This assignment is due Thursday, February 2.

This assignment makes use of files contained in this zip file. In the zip file there are four source files, BTree.java, BuildBTree.java, Tokenizer.java, and Traversal.java, that are completed for you. And there are another four source files, InfixPrinter.java, Evaluate.java, PrettyPrinter.java, and Main.java that you must complete. When you are finished, the output from your Main program should look exactly like the file output.txt.

Every arithmetic expression can be represented by a binary tree called its "expression tree". For example, the expression "2+3" has the expression tree

           +
         /   \
        2     3
and the slightly more complex expression "2 + 3 * 4" has the expression tree
           +
         /   \
        2     *      <====>    2 + 3 * 4
            /   \
           3     4
Notice how the operator with higher precedence appears lower in the tree. That represents the fact that you should evaluate that operator first. Now consider the slightly different expression "(2 + 3) * 4", in which the addition operator should be evaluated before the multiplication operator. This expression has the expression tree
           *
         /   \
       +      4      <====>    (2 + 3) * 4
     /   \
    2     3
Notice how the parentheses in the arithmetic expression help determine the structure of the expression tree, but the parentheses are not part of the expression tree.

Besides having an expression tree, every arithmetic expression also has a "prefix notation" and a "postfix notation". In prefix notation, operators are written before their operands. In postfix notation, operators are written after their operands. (In regular "infix notation", operators are written between their operands.)

For example, the expression "2+3" has the prefix notation "+ 2 3". The expression "2 + 3 * 4" has the prefix notation "+ 2 * 3 4". Notice that you read this as a plus operator with its first operand being 2 and its second operand being the prefix notation "* 3 4". As another example, the expression "(2 + 3) * 4" has prefix notation "* + 2 3 4". You read this as a multiplication operator with its first operand being the infix notation "+ 2 3", and its second operand being 4.

The expression "2+3" has the postfix notation "2 3 +". The expression "2 + 3 * 4" has the postfix notation "2 3 4 * +". You read postfix notation from right to left, so you read this as a plus operator with its second operand being the postfix notation "3 4 *" and its first operand being 2. As another example, the expression "(2 + 3) * 4" has postfix notation "2 3 + 4 *". You read this as a multiplication operator with its second operand being 4 and its first operand being the postfix notation "2 3 +". (Note: Even though you read postfix notation from right to left, you evaluate it from left to right.)

Prefix and postfix notations are hard for humans to read and write, but they are very useful for language processing programs, like compilers and interpreters. (One reason why they are so useful is that they never need parentheses.)

The goal of this assignment is to see how versions of preorder, inorder, and postorder traversal of a binary tree can do useful work for us when the binary tree is an expression tree. In summary:

  • The postorder traversal of an expression tree outputs the postfix notation for the expression.
  • A modified postorder traversal of an expression tree can evaluate the expression.
  • The preorder traversal of an expression tree outputs the prefix notation for the expression.
  • A modified preorder traversal of an expression tree can prettyprint the expression.
  • A modified inorder traversal of an expression tree can convert the expression tree into its original, parenthesized infix notation.

In the zip file there is a program Main.java which defines several arithmetic expressions as strings in prefix notation. For each of these arithmetic expressions, this program uses BuildBTree.java to build an expression tree for the arithmetic expression, then it uses InfixPrinter.java to print out the original parenthesized infix notation for the expression, then it uses Evaluate.java to compute the numeric value of the expression, and finally it uses PrettyPrinter.java to print the expression in a prettyprinted manner similar to Lisp programs.

The BuildBTree.java program is written for you, so you should not modify that file (it contains a method that takes as input a String holding the infix notation for an arithmetic expression, and returns the expression tree for the expression). You need to complete the three files InfixPrinter.java, Evaluate.java, and PrettyPrinter.java. You also need to write the infix string for a few of the arithmetic expressions in the file Main.java. When these files are completed and you run Main, its output should look exactly like the file output.txt.

The eval() method in the file Evaluate.java should be a modified postorder traversal of the expression tree. The main idea is that in order to evaluate the operator at the root of a sub tree, you must first (recursively) evaluate each of the operator's two sub trees (i.e., the operator's operands). After you get these two values, you need either a case statement, or nested if-else statements, that test which operator is at the root of the sub tree, and then use Java's version of that operator to compute the desired result.

The infixPrint() method in InfixPrinter.java is a bit more difficult than the eval() method. The infixPrint() method should be a modified inorder traversal of the expression tree. If this method were just a regular inorder traversal, the resulting string would be missing the parentheses that it needs to define the order of operations. For example, the inorder traversal of this expression tree

           *
         /   \
       +      4
     /   \
    2     3
is "2 + 3 * 4" but the correct parenthesized infix notation is "(2 + 3) * 4". So the infixPrint() method needs to figure out where to put parentheses. It does this using information about the precedence level of the arithmetic operators. Each operator has an integer precedence level. If an operator in a left sub tree has a lower precedence level than the operator at the root, then that left sub tree gets wrapped in a pair of parentheses. If an operator in a right sub tree has a lower, or equal, precedence level than the operator at the root, then that right sub tree gets wrapped in a pair of parentheses. For example, what would be the parenthesized infix notation for this expression tree?
           *
         /   \
       /       \
      +         *
    /   \     /   \
   2     3   4     5
In the file InfixPrinter.java there is a method precedence() that returns the precedence level of each operator. Use this method to help your infixPrint() method decide where to put in parentheses.

The prettyprinter for this assignment is slightly different from the ones in assignment 1. For example, consider this binary tree.

(-
  (+
    1
    (*
      2
      3
    )
  )
  (/
    (^
      4
      5
    )
    6
  )
)
The prettyprinter for this assignment should print this tree in the following compact form. As with the second prettyprinter in assignment 1, a sub tree of depth 1 should be printed on a single line. In addition, every sub tree should have it left child's root on the same line as the sub tree's root. Finally, closing parentheses should be placed at the end of a line. (This is the prettyprinting format used by languages like Lisp or Scheme.)
(- (+ 1
      (* 2 3))
   (/ (^ 4 5)
      6))

So you need to write one modified postorder traversal, one modified preorder traversal, and a modified inorder traversal. The easiest is the eval() method, so try that first. After you test it and it works, modify your second prettyprinter from assignment 1 to get the prettyprinter for this assignment. Last, work on the infixPrint() method.

Turn in a zip file called CS316Hw2Surname.zip (where Surname is your last name) containing the files BTree.java, BuildBTree.java, Tokenizer.java, and Traversal.java, and your final versions of the files PrettyPrinter.java, InfixPrinter.java, Evaluate.java, and Main.java.

Here are some references that you may find useful.

This assignment is due Thursday, February 2.


Return to the main homework page.
Return to the CS 31600 home page.


compliments and criticisms