Skip to content

refactor: PostfixToInfix #5409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 41 additions & 83 deletions src/main/java/com/thealgorithms/stacks/PostfixToInfix.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,125 +20,83 @@ public final class PostfixToInfix {
private PostfixToInfix() {
}

/**
* Determines if a given character is a valid arithmetic operator.
*
* @param token the character to check
* @return true if the character is an operator, false otherwise
*/
public static boolean isOperator(char token) {
switch (token) {
case '+':
case '-':
case '/':
case '*':
case '^':
return true;
default:
return false;
}
return token == '+' || token == '-' || token == '/' || token == '*' || token == '^';
}

/**
* Validates whether a given string is a valid postfix expression.
*
* A valid postfix expression must meet these criteria:
* 1. It should have at least one operator and two operands.
* 2. The number of operands should always be greater than the number of operators at any point in the traversal.
*
* @param postfix the postfix expression string to validate
* @return true if the expression is valid, false otherwise
*/
public static boolean isValidPostfixExpression(String postfix) {
/* Postfix expression length should NOT be less than 3 */
if (postfix.length() < 3) {
return false;
if (postfix.length() == 1 && (Character.isAlphabetic(postfix.charAt(0)))) {
return true;
}

/* First two characters should NOT be operators */
if (isOperator(postfix.charAt(0))) {
return false;
}
if (isOperator(postfix.charAt(1))) {
return false;
if (postfix.length() < 3) {
return false; // Postfix expression should have at least one operator and two operands
}

int operandCount = 0;
int operatorCount = 0;

/* Traverse the postfix string to check if --> Number of operands = Number of operators + 1
*/
for (int i = 0; i < postfix.length(); i++) {
char token = postfix.charAt(i);

for (char token : postfix.toCharArray()) {
if (isOperator(token)) {
operatorCount++;
if (operatorCount >= operandCount) {
return false;
return false; // Invalid: more operators than operands at any point
}
} else {
if (operatorCount == 0) {
operandCount++;
continue;
}

if (operandCount != operatorCount + 1) {
return false;
}

/* Operand count is set to 2 because:-
*
* 1) the previous set of operands & operators combined have become a single valid
* expression, which could be considered/assigned as a single operand.
*
* 2) the operand in the current iteration.
*/
operandCount = 2;

/* Reset operator count */
operatorCount = 0;
operandCount++;
}
}

return (operandCount == operatorCount + 1);
return operandCount == operatorCount + 1;
}

/**
* Converts a valid postfix expression to an infix expression.
*
* @param postfix the postfix expression to convert
* @return the equivalent infix expression
* @throws IllegalArgumentException if the postfix expression is invalid
*/
public static String getPostfixToInfix(String postfix) {
String infix = "";

if (postfix.isEmpty()) {
return infix;
return "";
}

/* Validate Postfix expression before proceeding with the Infix conversion */
if (!isValidPostfixExpression(postfix)) {
throw new IllegalArgumentException("Invalid Postfix Expression");
}

Stack<String> stack = new Stack<>();
StringBuilder valueString = new StringBuilder();

String operandA;
String operandB;
char operator;

for (int index = 0; index < postfix.length(); index++) {
char token = postfix.charAt(index);

for (char token : postfix.toCharArray()) {
if (!isOperator(token)) {
stack.push(Character.toString(token));
continue;
} else {
String operandB = stack.pop();
String operandA = stack.pop();
valueString.append('(').append(operandA).append(token).append(operandB).append(')');
stack.push(valueString.toString());
valueString.setLength(0);
}

operator = token;
operandB = stack.pop();
operandA = stack.pop();

valueString.append('(');

valueString.append(operandA);
valueString.append(operator);
valueString.append(operandB);

valueString.append(')');

stack.push(valueString.toString());
valueString.setLength(0);
}

infix = stack.pop();
return infix;
}

public static void main(String[] args) {
assert getPostfixToInfix("ABC+/").equals("(A/(B+C))");
assert getPostfixToInfix("AB+CD+*").equals("((A+B)*(C+D))");
assert getPostfixToInfix("AB+C+D+").equals("(((A+B)+C)+D)");
assert getPostfixToInfix("ABCDE^*/-").equals("(A-(B/(C*(D^E))))");
assert getPostfixToInfix("AB+CD^/E*FGH+-^").equals("((((A+B)/(C^D))*E)^(F-(G+H)))");
return stack.pop();
}
}
33 changes: 33 additions & 0 deletions src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.thealgorithms.stacks;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class PostfixToInfixTest {

@ParameterizedTest
@MethodSource("provideValidPostfixToInfixTestCases")
void testValidPostfixToInfixConversion(String postfix, String expectedInfix) {
assertEquals(expectedInfix, PostfixToInfix.getPostfixToInfix(postfix));
}

static Stream<Arguments> provideValidPostfixToInfixTestCases() {
return Stream.of(Arguments.of("A", "A"), Arguments.of("ABC+/", "(A/(B+C))"), Arguments.of("AB+CD+*", "((A+B)*(C+D))"), Arguments.of("AB+C+D+", "(((A+B)+C)+D)"), Arguments.of("ABCDE^*/-", "(A-(B/(C*(D^E))))"), Arguments.of("AB+CD^/E*FGH+-^", "((((A+B)/(C^D))*E)^(F-(G+H)))"));
}

@Test
void testEmptyPostfixExpression() {
assertEquals("", PostfixToInfix.getPostfixToInfix(""));
}

@Test
void testNullPostfixExpression() {
assertThrows(NullPointerException.class, () -> PostfixToInfix.getPostfixToInfix(null));
}
}