|
4 | 4 | import java.util.regex.Matcher;
|
5 | 5 | import java.util.regex.Pattern;
|
6 | 6 |
|
| 7 | +/** |
| 8 | + * Utility class for converting an infix arithmetic expression |
| 9 | + * into its equivalent prefix notation expression. |
| 10 | + * <p> |
| 11 | + * This class provides a static method to perform the conversion, |
| 12 | + * validating balanced brackets before processing. |
| 13 | + * </p> |
| 14 | + */ |
7 | 15 | public final class InfixToPrefix {
|
| 16 | + |
8 | 17 | private InfixToPrefix() {
|
9 | 18 | }
|
10 | 19 |
|
11 | 20 | /**
|
12 |
| - * Convert an infix expression to a prefix expression using stack. |
| 21 | + * Converts a given infix expression string to a prefix expression string. |
| 22 | + * <p> |
| 23 | + * The method validates that the input expression has balanced brackets using |
| 24 | + * {@code BalancedBrackets.isBalanced} on the filtered bracket characters. |
| 25 | + * It throws an {@code IllegalArgumentException} if the brackets are unbalanced, |
| 26 | + * and a {@code NullPointerException} if the input is null. |
| 27 | + * </p> |
| 28 | + * <p> |
| 29 | + * Supported operators: {@code +, -, *, /, ^} and operands can be letters or digits. |
| 30 | + * </p> |
13 | 31 | *
|
14 |
| - * @param infixExpression the infix expression to convert |
15 |
| - * @return the prefix expression |
16 |
| - * @throws IllegalArgumentException if the infix expression has unbalanced brackets |
17 |
| - * @throws NullPointerException if the infix expression is null |
| 32 | + * @param infixExpression the arithmetic expression in infix notation |
| 33 | + * @return the equivalent prefix notation expression |
| 34 | + * @throws IllegalArgumentException if brackets are unbalanced |
| 35 | + * @throws NullPointerException if the input expression is null |
18 | 36 | */
|
19 |
| - public static String infix2Prefix(String infixExpression) throws IllegalArgumentException { |
| 37 | + public static String infix2Prefix(String infixExpression) { |
20 | 38 | if (infixExpression == null) {
|
21 | 39 | throw new NullPointerException("Input expression cannot be null.");
|
22 | 40 | }
|
| 41 | + |
23 | 42 | infixExpression = infixExpression.trim();
|
24 | 43 | if (infixExpression.isEmpty()) {
|
25 | 44 | return "";
|
26 | 45 | }
|
| 46 | + |
27 | 47 | if (!BalancedBrackets.isBalanced(filterBrackets(infixExpression))) {
|
28 | 48 | throw new IllegalArgumentException("Invalid expression: unbalanced brackets.");
|
29 | 49 | }
|
30 | 50 |
|
31 | 51 | StringBuilder output = new StringBuilder();
|
32 |
| - Stack<Character> stack = new Stack<>(); |
33 |
| - // Reverse the infix expression for prefix conversion |
| 52 | + Stack<Character> operatorStack = new Stack<>(); |
| 53 | + |
| 54 | + // Reverse the infix expression to facilitate prefix conversion |
34 | 55 | String reversedInfix = new StringBuilder(infixExpression).reverse().toString();
|
35 |
| - for (char element : reversedInfix.toCharArray()) { |
36 |
| - if (Character.isLetterOrDigit(element)) { |
37 |
| - output.append(element); |
38 |
| - } else if (element == ')') { |
39 |
| - stack.push(element); |
40 |
| - } else if (element == '(') { |
41 |
| - while (!stack.isEmpty() && stack.peek() != ')') { |
42 |
| - output.append(stack.pop()); |
| 56 | + |
| 57 | + for (char token : reversedInfix.toCharArray()) { |
| 58 | + if (Character.isLetterOrDigit(token)) { |
| 59 | + // Append operands directly to output |
| 60 | + output.append(token); |
| 61 | + } else if (token == ')') { |
| 62 | + // Push ')' onto stack (since expression is reversed, '(' and ')' roles swapped) |
| 63 | + operatorStack.push(token); |
| 64 | + } else if (token == '(') { |
| 65 | + // Pop operators until ')' is found |
| 66 | + while (!operatorStack.isEmpty() && operatorStack.peek() != ')') { |
| 67 | + output.append(operatorStack.pop()); |
43 | 68 | }
|
44 |
| - stack.pop(); |
| 69 | + operatorStack.pop(); // Remove the ')' |
45 | 70 | } else {
|
46 |
| - while (!stack.isEmpty() && precedence(element) < precedence(stack.peek())) { |
47 |
| - output.append(stack.pop()); |
| 71 | + // Pop operators with higher precedence before pushing current operator |
| 72 | + while (!operatorStack.isEmpty() && precedence(token) < precedence(operatorStack.peek())) { |
| 73 | + output.append(operatorStack.pop()); |
48 | 74 | }
|
49 |
| - stack.push(element); |
| 75 | + operatorStack.push(token); |
50 | 76 | }
|
51 | 77 | }
|
52 |
| - while (!stack.isEmpty()) { |
53 |
| - output.append(stack.pop()); |
| 78 | + |
| 79 | + // Append any remaining operators in stack |
| 80 | + while (!operatorStack.isEmpty()) { |
| 81 | + output.append(operatorStack.pop()); |
54 | 82 | }
|
55 | 83 |
|
56 |
| - // Reverse the result to get the prefix expression |
| 84 | + // Reverse the output to obtain the final prefix expression |
57 | 85 | return output.reverse().toString();
|
58 | 86 | }
|
59 | 87 |
|
60 | 88 | /**
|
61 |
| - * Determines the precedence of an operator. |
| 89 | + * Returns the precedence level of the given operator. |
62 | 90 | *
|
63 |
| - * @param operator the operator whose precedence is to be determined |
64 |
| - * @return the precedence of the operator |
| 91 | + * @param operator the operator character (e.g., '+', '-', '*', '/', '^') |
| 92 | + * @return the precedence value: higher means higher precedence, |
| 93 | + * or -1 if the character is not a recognized operator |
65 | 94 | */
|
66 | 95 | private static int precedence(char operator) {
|
67 |
| - switch (operator) { |
68 |
| - case '+': |
69 |
| - case '-': |
70 |
| - return 0; |
71 |
| - case '*': |
72 |
| - case '/': |
73 |
| - return 1; |
74 |
| - case '^': |
75 |
| - return 2; |
76 |
| - default: |
77 |
| - return -1; |
78 |
| - } |
| 96 | + return switch (operator) { |
| 97 | + case '+', '-' -> 0; |
| 98 | + case '*', '/' -> 1; |
| 99 | + case '^' -> 2; |
| 100 | + default -> -1; |
| 101 | + }; |
79 | 102 | }
|
80 | 103 |
|
81 | 104 | /**
|
82 |
| - * Filters out all characters from the input string except brackets. |
| 105 | + * Extracts only the bracket characters from the input string. |
| 106 | + * Supports parentheses (), curly braces {}, square brackets [], and angle brackets <>. |
83 | 107 | *
|
84 |
| - * @param input the input string to filter |
85 |
| - * @return a string containing only brackets from the input string |
| 108 | + * @param input the original expression string |
| 109 | + * @return a string containing only bracket characters from the input |
86 | 110 | */
|
87 | 111 | private static String filterBrackets(String input) {
|
88 | 112 | Pattern pattern = Pattern.compile("[^(){}\\[\\]<>]");
|
|
0 commit comments