Skip to content

Commit 282e696

Browse files
committed
Reverse Polish Notation
1 parent 6f46fe9 commit 282e696

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

src/hard/ReversePolishNotation.java

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package hard;
2+
3+
import java.util.Stack;
4+
import java.util.regex.Matcher;
5+
import java.util.regex.Pattern;
6+
7+
/**
8+
* Have the function ReversePolishNotation(str) read str which will be an arithmetic expression
9+
* composed of only integers and the operators: +,-,* and /
10+
* and the input expression will be in postfix notation (Reverse Polish notation),
11+
* an example: (1 + 2) * 3 would be 1 2 + 3 * in postfix notation.
12+
* Your program should determine the answer for the given postfix expression.
13+
* For example: if str is 2 12 + 7 / then your program should output 2.
14+
*/
15+
class ReversePolishNotation {
16+
17+
private static final Stack<String> stack = new Stack<>();
18+
19+
/**
20+
* Cleaning, parsing and splitting the input string into the array of tokens.
21+
* 1. convert to lowercase
22+
* 2. remove multiple spaces
23+
* 3. split two bundled characters (but only when the second character is not a digit)
24+
* 4. split bundled command/operator and a digit. Excl. minus to keep negative numbers intact.
25+
* 5. trim (remove leading and trailing spaces)
26+
* 6. split to array of strings
27+
*
28+
* @param input input string
29+
* @return String array with parsed operators and operands
30+
*/
31+
private static String[] parseConsoleInput(String input) {
32+
return input
33+
.toLowerCase()
34+
.replaceAll(" +", " ")
35+
.replaceAll("([\\p{P}\\p{S}a-z0-9])(?=[\\p{P}\\p{S}a-z])", "$1 ")
36+
.replaceAll("([^- 0-9])(?=[0-9])", "$1 ")
37+
.trim()
38+
.split(" ");
39+
}
40+
41+
/**
42+
* Prints out a message to the console if the user input is invalid.
43+
*
44+
* @param op single element of the input string
45+
*/
46+
private static void printInputError(String op) {
47+
System.out.println("Unrecognised operator or operand: \"" + op + "\".");
48+
}
49+
50+
/**
51+
* Reduces two operands to a single result
52+
* by performing an arithmetical operation.
53+
*
54+
* @param a operand A
55+
* @param b operand B
56+
* @param operator denotes operation type (addition, substraction, division etc.)
57+
* @return result of the arithmetical operation
58+
* @throws ArithmeticException if divisor is 0
59+
*/
60+
public static long reduceOperands(long a, long b, String operator) {
61+
switch (operator) {
62+
case "+":
63+
return a + b;
64+
case "-":
65+
return a - b;
66+
case "*":
67+
return a * b;
68+
case "/":
69+
if (b == 0) {
70+
System.out.println("Divide by 0.");
71+
throw new ArithmeticException();
72+
}
73+
return a / b;
74+
default:
75+
return 0;
76+
}
77+
}
78+
79+
/**
80+
* Checks if the token is an operand (0-9).
81+
*
82+
* @param op a single token from the input string
83+
* @return true if the token is an operand, false if not
84+
*/
85+
private static boolean isOperand(String op) {
86+
Pattern pattern = Pattern.compile("^[\\d]|^-[\\d]");
87+
Matcher matcher = pattern.matcher(op);
88+
return matcher.find();
89+
}
90+
91+
/**
92+
* Checks if the token is an operator + - * / : ^ ( ) etc.
93+
*
94+
* @param op a single token from the input string
95+
* @return true if the token is an operator, false if not
96+
*/
97+
private static boolean isOperator(String op) {
98+
Pattern pattern = Pattern.compile("^[+\\-*/^%]");
99+
Matcher matcher = pattern.matcher(op);
100+
return matcher.find();
101+
}
102+
103+
/**
104+
* Takes two operands from stack and perform the operation with a provider operator.
105+
*
106+
* @param operator denotes operation type (addition, substraction, division etc.)
107+
* @return result of the evaluation
108+
*/
109+
private static String performArithOperation(String operator) {
110+
if (stack.size() >= 2) {
111+
// Safe to evaluate
112+
String elementB = stack.pop();
113+
String elementA = stack.pop();
114+
long opB = Long.parseLong(elementB);
115+
long opA = Long.parseLong(elementA);
116+
long result = reduceOperands(opA, opB, operator);
117+
return Long.toString(result);
118+
} else {
119+
// Stack underflow since at least one element is null
120+
return null;
121+
}
122+
}
123+
124+
/**
125+
* Computes the entire expression in Reverse Polish Notation.
126+
*
127+
* @param tokens expression tokens that are already parsed and split to Array of Strings
128+
*/
129+
private static Long evaluateExpression(String[] tokens) {
130+
for (String token : tokens) {
131+
// token is an operand, push it to stack and move on
132+
if (isOperand(token)) {
133+
stack.push(token);
134+
continue;
135+
}
136+
// token is an operator, evaluate
137+
if (isOperator(token)) {
138+
String result = performArithOperation(token);
139+
if (result != null) {
140+
stack.push(result);
141+
}
142+
continue;
143+
}
144+
// token is illegal
145+
printInputError(token);
146+
}
147+
// all tokens have been processed
148+
if (stack.isEmpty()) {
149+
return null;
150+
}
151+
return Long.parseLong(stack.peek());
152+
}
153+
154+
public static String reversePolishNotation(String expression) {
155+
String[] tokens = parseConsoleInput(expression);
156+
Long result = evaluateExpression(tokens);
157+
return result == null ? "" : result.toString();
158+
}
159+
160+
/**
161+
* Entry point.
162+
*
163+
* @param args command line arguments
164+
*/
165+
public static void main(String[] args) {
166+
String expression = "1 1 + 1 + 1 +";
167+
String result = reversePolishNotation(expression);
168+
System.out.println(result);
169+
}
170+
171+
}

0 commit comments

Comments
 (0)