GeeksforGeeks Medium

Infix to Postfix Conversion

Convert an infix arithmetic expression to its postfix (Reverse Polish) form.

Constraints
  • 1 <= |expression| <= 10⁵
  • Operators: + - * / ^ with brackets
  • ^ is right-associative, unlike the others
stackstringsparsing
Open on GeeksforGeeks ↗
02

Intuition

Infix to postfix conversion rewrites an ordinary expression like a + b * c into a b c * +, where operators follow their operands. Postfix needs no parentheses and no precedence rules, which is why compilers and calculators use it internally. The conversion is Shunting-yard, and its organising idea is that operands can be emitted immediately while operators must wait: - Output operands as they appear; hold operators on a stack until an operator of lower or equal precedence forces them out. An operand's position in the output is already known, but an operator's is not — it depends on what follows. On reading an operator, pop from the stack every operator with greater or equal precedence before pushing the new one. That popping is what places higher-precedence operations earlier in the output, so a + b * c keeps * ahead of +. Associativity changes that comparison. Left-associative operators pop on equal precedence, so a - b - c becomes a b - c -. Right-associative operators like exponentiation must not, or 2 ^ 3 ^ 2 is grouped the wrong way. Using the same rule for both is the standard bug. Parentheses are handled directly: push (, and on ) pop until the matching ( is found. Neither bracket is ever written to the output — their entire effect is already captured by the ordering they forced. At the end, pop any operators still on the stack. Forgetting this drops the final operators, which is easy to miss because short test expressions often end with an operand.

How to spot this pattern

The shunting-yard algorithm. Operands pass straight through; operators wait on a stack until something of equal or higher precedence arrives, then pop. Parentheses act as barriers the popping loop refuses to cross, which is what makes grouping work without recursion.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what unresolved thing you are holding, and what event finally resolves it. Aim for O(n) time and O(n) space.

1

Emit operands immediately

An operand's output position is already known, but an operator's depends on what comes after it. That asymmetry is why operators need a stack and operands do not.

2

Pop before pushing an operator

On reading an operator, pop every stacked operator with greater or equal precedence first. This places higher-precedence operations earlier in the output.

3

Distinguish associativity

Left-associative operators pop on equal precedence, so a - b - c becomes a b - c -. Right-associative ones like ^ must not, or 2 ^ 3 ^ 2 groups incorrectly.

4

Push an opening parenthesis

Place ( on the stack as a barrier. Operators above it are enclosed by the group and must be emitted before anything outside it.

5

Unwind on a closing parenthesis

Pop and output until the matching ( is reached, then discard both. Neither bracket ever appears in the output — their effect lives entirely in the ordering.

6

Flush the stack at the end

Pop any remaining operators after the input is exhausted. Forgetting this drops the final operators, and short test expressions often hide the bug.

7

Cost of the conversion

Each token is pushed and popped at most once, giving O(n) time and O(n) space for the stack in the worst case of deep nesting.

04

Solution & live demo

▶1class Solution:
▶2 def infixToPostfix(self, exp):
▶3 prec = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}
▶4 out, st = [], []
▶5 for ch in exp:
▶6 if ch.isalnum():
▶7 out.append(ch)
▶8 elif ch == '(':
▶9 st.append(ch)
▶10 elif ch == ')':
▶11 while st and st[-1] != '(':
▶12 out.append(st.pop())
▶13 st.pop()
▶14 else:
▶15 while st and st[-1] != '(' and prec.get(st[-1], 0) >= prec[ch]:
▶16 out.append(st.pop())
▶17 st.append(ch)
▶18 while st:
▶19 out.append(st.pop())
▶20 return ''.join(out)
05

Common pitfalls

Popping past an opening parenthesis

✗ Wrong
while st and prec.get(st[-1], 0) >= prec[ch]:
✓ Right
while st and st[-1] != '(' and prec.get(st[-1], 0) >= prec[ch]:

( has no precedence and must block the pop loop — it marks where the current group began. Without the guard, operators from an enclosing group get emitted inside the inner one.

Leaving the opening parenthesis on the stack

✗ Wrong
while st and st[-1] != '(':
    out.append(st.pop())
✓ Right
while st and st[-1] != '(':
    out.append(st.pop())
st.pop()

The ( is a marker, not output. Failing to discard it after the group closes leaves it on the stack, where the final drain appends it to the result.

Treating right-associative ^ like the others

✗ Wrong
prec[st[-1]] >= prec[ch]   # for ^ too
✓ Right
# for right-associative operators use > rather than >=

2^3^2 must group as 2^(3^2), so an equal-precedence ^ on the stack should not pop. Using >= uniformly makes exponentiation left-associative and silently changes the meaning.

06

Edge cases

Single operand

It is emitted directly and the stack stays empty.

Fully bracketed expression

The brackets are consumed and never appear in the output.

Right-associative ^

Needs a strict > comparison; using >= produces the wrong association for a^b^c.

Operators of equal precedence

Left-associative ones pop on >=, giving the correct left-to-right grouping.

07

Complexity

Time
O(n)
Space
O(n)
Each character is pushed and popped at most once.