We are accustomed to reading and writing arithmetic expressions that are in infix form, such as 13 * (4 + 7) - 2. In such expressions, each binary operator appears between its two operands. In this example, the first (or left) operand of the multiplication operator * is the atomic expression 13 and its second (or right) operand is the compound expression (4 + 7). (An atomic expression is a naked numeric literal, whereas a compound expression is one in which at least one operator is present.)
Two other well-known forms of expressions are prefix and postfix. As its name suggests, in a prefix (respectively, postfix) expression each operator precedes (respectively, follows) its two operands. To illustrate, below is a table in which each row shows the same expression in its three different forms.
Infix | Prefix | Postfix |
---|---|---|
15 | 15 | 15 |
18 + 5 | + 18 5 | 18 5 + |
13 * (4 + 7) − 2 | − * 13 + 4 7 2 | 13 4 7 + * 2 − |
(3 + 7) * 2 − 24 / (1 + 5) | − * + 3 7 2 / 24 + 1 5 | 3 7 + 2 * 24 1 5 + / − |
You will notice that, in the examples above, the prefix and postfix forms have no parentheses. This is not a coincidence; rather, it is a property of those forms that parentheses are never needed. This gives them an advantage over the infix form, in which it is often necessary to use parentheses —and to rely upon the conventions of operator precedence (PEMDAS) where parentheses are omitted— to make expressions unambiguous.
Using parentheses to more clearly show the association between operators and their operands in the most complicated expression above, we would have
Formally, compound expressions in the three forms have the following syntax (where <op> is an operator, such as +):
- / \ / \ / \ / \ * ÷ / \ / \ / \ / \ + 2 24 + / \ / \ 3 7 1 5 |
Infix: | ( <infix-expr> <op> <infix-expr> ) |
---|---|
Prefix: | <op> <prefix-expr> <prefix-expr> |
Postfix: | <postfix-expr> <postfix-expr> <op> |
Compound expressions are hierarchical and recursive in that they contain as components other expressions, which are typically referred to as subexpressions. To show the hierarchical structure of an expression more clearly, it can be rendered as an expression-tree. The leaves of such a tree correspond to the atomic subexpressions that appear within the expression and the subtrees rooted at interior nodes correspond to the compound subexpressions within the expression. As an example, consider the expression-tree to the right, which is yet another representation of the expression whose infix form is (3+7)*2 − 24/(1+5).
The student is responsible for completing three methods in this class. One of them, valueOf(), computes the value of the represented expression. The other two produce, respectively, the prefix and postfix expression-strings describing the represented expression. (Suggestion: For these two, use as a model the method that produces the infix expression-string.)
To earn a few points of extra credit, the student is invited to augment this class so that it supports not only the four basic arithmetic operators (i.e., '+', '-', '*', and '/') but also '%' (remainder).
The student is responsible for completing each of the methods mentioned above. Below are algorithms, described in pseudocode, that the student can use as the basis for the Java code in those methods.
As usual, user input is shown in boldface. Notice that the program expects consecutive tokens to be separated from each other by at least one space.
Welcome to the Expression Tree Tester Program (0) Quit (1) Infix Expression (2) Prefix Expression (3) Postfix Expression > 1 Enter infix expression: ( ( ( 3 + 7 ) * 2 ) - ( 1 + 5 ) ) Value is 14 Prefix form: - * + 3 7 2 + 1 5 Infix form: ( ( ( 3 + 7 ) * 2 ) - ( 1 + 5 ) ) Postfix form: 3 7 + 2 * 1 5 + - (0) Quit (1) Infix Expression (2) Prefix Expression (3) Postfix Expression > 2 Enter prefix expression: + / 12 4 * 9 1 Value is 12 Prefix form: + / 12 4 * 9 1 Infix form: ( ( 12 / 4 ) + ( 9 * 1 ) ) Postfix form: 12 4 / 9 1 * + (0) Quit (1) Infix Expression (2) Prefix Expression (3) Postfix Expression > 3 Enter postfix expression: 1 2 3 4 * 5 - + / Value is 0 Prefix form: / 1 + 2 - * 3 4 5 Infix form: ( 1 / ( 2 + ( ( 3 * 4 ) - 5 ) ) ) Postfix form: 1 2 3 4 * 5 - + / (0) Quit (1) Infix Expression (2) Prefix Expression (3) Postfix Expression > 0 Goodbye from the Expression Tree Tester Program |
Each of the three stubbed methods in the provided ExprTreeBuilder class is intended to accept an expression-string and to produce as its result the corresponding expression-tree (which, of course, will be an instance of (one of the child classes of) ExprTree). As its name suggests, fromInfix() handles expression-strings in infix form, while fromPostfix() and fromPrefix() handle expression-strings in, respectively, postfix and prefix forms.
To keep things simple, the fromInfix() method assumes that the expression-string given to it is fully-parenthesized (i.e., is an FPAE). You are already familiar with an algorithm that evaluates an FPAE. An algorithm for building an expression tree from an FPAE is very similar and appears below. Use it as a guide in completing the fromInfix() method.
operandStk = empty stack operatorStk = empty stack do while there are more tokens to process token = next token if token is a left parenthesis do nothing else if token is a numeral k = integer value described by token (use Integer.parseInt()) e = atomic expression tree holding value k operandStk.push(e) else if token is an operator operatorStk.push(token) else // token is a right parenthesis operand2 = operandStk.pop() operand1 = operandStk.pop() operator = operatorStk.pop() e = compound expression tree whose components are operator, operand1, and operand2 operandStk.push(e) fi od return lone expression tree on operandStk |
An algoritm for transforming a postfix expression-string into an expression-tree follows. Use it as a guide in completing the fromPostfix() method. Below that is a diagram that traces execution of the algorithm when fed as input the postfix expression-string 3 7 + 2 * 24 1 5 + / −. The diagram shows the contents of the stack after each token of the expression is processed. (Note that each element on the stack is an expression-tree.)
stk = empty stack do while there are more tokens to process token = next token if token is a numeral k = integer value described by token e = atomic expression tree holding value k stk.push(e) else // token is an operator operand2 = stk.pop() operand1 = stk.pop() e = compound expression tree whose components are the token, operand1, and operand2 stk.push(e) fi od return lone expression tree remaining on stk |
| | | 3 | +---+ |
| | | 7 | |---| | 3 | +---+ |
| | | + | | / \ | | 3 7 | +-------+ |
| | | 2 | |-------| | + | | / \ | | 3 7 | +-------+ |
| | | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +-----------+ |
| | | 24 | |-----------| | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +-----------+ |
| | | 1 | |-----------| | 24 | |-----------| | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +-----------+ |
| | | 5 | |-----------| | 1 | |-----------| | 24 | |-----------| | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +-----------+ |
| | | + | | / \ | | 1 5 | |-----------| | 24 | |-----------| | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +-----------+ |
| ÷ | | / \ | | / + | | / / \ | | 24 1 5 | |------------| | * | | / \ | | / \ | | + 2 | | / \ | | 3 7 | +------------+ |
| | | - | | / \ | | / \ | | / \ | | / \ | | * ÷ | | / \ / \ | | / \ / \ | | + 2 24 + | | / \ / \ | | 3 7 1 5 | +-----------------------+ |