Problem

Convert an infix expression (which uses the standard order of operations with parentheses) to a postfix expression (Reverse Polish Notation).

Pre-requisite - What is infix, postfix and prefix?

What is infix, postfix and prefix?

Examples

Example 1:

Input: "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
Output: "3 4 2 * 1 5 - 2 3 ^ ^ / +"
Explanation: The infix expression "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3" is converted to the postfix expression "3 4 2 * 1 5 - 2 3 ^ ^ / +".

Example 2:

Input: "a + b * c"
Output: "a b c * +"
Explanation: The infix expression "a + b * c" is converted to the postfix expression "a b c * +".

Evaluation

Once the conversion, we can evaluate the expression using the stack again.

Solution

Method 1 - Shunting-yard Algorithm using Stacks

I used a stack ADT implemented with a linked list, as linked lists allow for dynamic memory allocation.

Converting an infix expression to Reverse Polish Notation can be achieved using the Shunting-yard algorithm by Dijkstra. This algorithm operates in O(n) time and requires O(n) space.

The method in this context follows the standard algorithm of comparing precedence between input and stack operators.

Pseudocode

  • While tokens are available:
    • Read the next token.
    • If it’s a number, add it to the output queue.
    • If it’s a function token, push it onto the stack.
    • If it’s a function argument separator (e.g., a comma):
      • Pop operators from the stack to the output queue until a left parenthesis is encountered. If none is found, the separator was misplaced or the parentheses mismatched.
    • If it’s an operator (o1):
      • While there’s an operator (o2) on top of the stack and either:
        • o1 is left-associative and has precedence less than or equal to o2,
        • or o1 is right associative and has less precedence than o2,
        • pop o2 off the stack and onto the output queue.
      • Push o1 onto the operator stack.
    • If it’s a left parenthesis "(", push it onto the stack.
    • If it’s a right parenthesis ")":
      • Pop operators from the stack to the output queue until a left parenthesis is encountered.
      • Remove the left parenthesis from the stack but do not add it to the output queue.
      • If a function token is on top of the stack, pop it onto the output queue.
      • If the stack is exhausted without finding a left parenthesis, the parentheses are mismatched.
  • When no more tokens are left:
    • While there are operator tokens on the stack:
      • If the top stack operator is a parenthesis, the parentheses are mismatched.
      • Pop operators from the stack to the output queue.
  • Exit.

Operator precedence

The precedence values used in the code are as follows:

OperatorPrecedence Value
^4
*3
/3
+2
-2

Code

Java
public class Solution {
    public String toPostfix(String infix) {
        StringBuilder output = new StringBuilder();
        Stack<Character> operators = new Stack<>();

        Map<Character, Integer> precedence = new HashMap<>();
        precedence.put('^', 4);
        precedence.put('*', 3);
        precedence.put('/', 3);
        precedence.put('+', 2);
        precedence.put('-', 2);

        Map<Character, Boolean> rightAssociative = new HashMap<>();
        rightAssociative.put('^', true); // '^' is right associative

        for (int i = 0; i < infix.length(); i++) {
            char token = infix.charAt(i);

            if (Character.isWhitespace(token)) {
                continue;
            } else if (Character.isLetterOrDigit(token)) {
                output.append(token).append(' ');
            } else if (token == '(') {
                operators.push(token);
            } else if (token == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    output.append(operators.pop()).append(' ');
                }
                operators.pop(); // Remove the '('
            } else if (precedence.containsKey(token)) {
                while (!operators.isEmpty() && precedence.containsKey(operators.peek()) &&
                        ((rightAssociative.getOrDefault(token, false) && precedence.get(token) < precedence.get(operators.peek())) ||
                        (!rightAssociative.getOrDefault(token, false) && precedence.get(token) <= precedence.get(operators.peek())))) {
                    output.append(operators.pop()).append(' ');
                }
                operators.push(token);
            }
        }

        while (!operators.isEmpty()) {
            output.append(operators.pop()).append(' ');
        }

        return output.toString().trim();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.toPostfix("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3")); // Output: 3 4 2 * 1 5 - 2 3 ^ ^ / +
        System.out.println(solution.toPostfix("a + b * c")); // Output: a b c * +
    }
}
Python
class Solution:
    def toPostfix(self, infix: str) -> str:
        output: List[str] = []
        operators: List[str] = []

        precedence = {'^': 4, '*': 3, '/': 3, '+': 2, '-': 2}
        right_associative = {'^': True}  # '^' is right associative

        i = 0
        while i < len(infix):
            token = infix[i]

            if token.isspace():
                i += 1
                continue
            elif token.isalnum():  # token is an operand
                output.append(token)
            elif token == '(':
                operators.append(token)
            elif token == ')':
                while operators and operators[-1] != '(':
                    output.append(operators.pop())
                operators.pop()  # Remove the '('
            elif token in precedence:
                while (operators and operators[-1] in precedence and
                    ((token in right_associative and precedence[token] < precedence[operators[-1]]) or
                        (token not in right_associative and precedence[token] <= precedence[operators[-1]]))):
                    output.append(operators.pop())
                operators.append(token)
            i += 1

        while operators:
            output.append(operators.pop())

        return ' '.join(output)

# Test the implementation
sol = Solution()
print(sol.toPostfix("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"))  # Output: 3 4 2 * 1 5 - 2 3 ^ ^ / +
print(sol.toPostfix("a + b * c"))  # Output: a b c * +

Complexity

  • ⏰ Time complexity: O(n) where n is the length of the infix expression.
  • 🧺 Space complexity: O(n) due to the use of an auxiliary stack.

Dry Run

Lets take some examples.

Example 1 - A*B+C

The expression A * B + C should convert to A B * C +.

StepCurrent SymbolOperator StackPostfix String
1AA
2**A
3B*A B
4++A B * {pop * then push +}
5C+A B * C
6A B * C +
Example 2 - A+B*C

The expression A + B * C should convert to A B C * +.

StepCurrent SymbolOperator StackPostfix String
1AA
2++A
3B+A B
4*+ *A B
5C+ *A B C
6A B C * +
Example 3 - a/b*(c+(d-e))

The expression a / b * ( c + ( d - e )) should convert to a b / c d e - + *.

StepCurrent SymbolOperator StackPostfix String
1aa
2//a
3b/a b
4**a b /
5(* (a b /
6c* (a b / c
7+* ( +a b / c
8(* ( + (a b / c
9d* ( + (a b / c d
10-* ( + ( -a b / c d
11e* ( + ( -a b / c d e
12)* ( +a b / c d e -
13)*a b / c d e - +
14a b / c d e - + *