This assignment makes use of the files contained in this zip file. This assignment is due Tuesday, February 13.
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.
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 Hw2.java
that you must complete. When you are finished, the output from your Hw2.java
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:
In the zip file there is a program Hw2.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 prefix 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 prefix string for a few of the arithmetic expressions in the file Hw2.java
. When these files are completed and you run Hw2
, 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 third prettyprinter in assignment 1, a sub tree of depth 1 should be printed on a single line and every sub tree should have it left child's root on the same line as the sub tree's root. In addition, for this prettyprinter, 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 a modified postorder traversal, a 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 third prettyprinter from assignment 1 to get the prettyprinter for this assignment. Last, work on the infixPrint()
method.
Turn in a zip file called CS316Hw1Surname.zip
(where Surname
is your last name) containing your versions of PrettyPrinter.java
, InfixPrinter.java
, Evaluate.java
, and Hw2.java
. Be sure to put your name and email address in every file your turn in.
This assignment is due Tuesday, February 13.
Here are some references that you may find useful.