Infix to Postfix Conversion Algorithm

The complete algorithm — seven rules, the precedence table, pseudocode you can translate into any language, a dry run, and the complexity analysis your exam will ask for.

Try it on your own expression

The idea in one paragraph

The algorithm is Dijkstra's shunting-yard, published in 1961 and named after a railway shunting yard: incoming operators are shunted onto a siding (the stack) and released onto the main line (the output) in the right order. Operands are never ambiguous, so they are written out the instant they are read. Operators are always ambiguous — something stronger might follow — so they are held until the algorithm can prove their position is fixed.

The algorithm, step by step

Create an empty operator stack and an empty output list, then scan the infix expression from left to right, one token at a time.

  1. If the token is an operand, append it to the output. Its position in postfix is already final, so it never goes on the stack.
  2. If the token is an opening bracket, push it. It is a marker, not an operator — it will be discarded, never output.
  3. If the token is a closing bracket, pop to the output until the matching opening bracket is on top. Then pop that opening bracket and discard both. If the stack empties before an opening bracket is found, the expression is unbalanced.
  4. If the token is an operator, pop while the top of the stack outranks it. Keep popping to the output while the stack is non-empty, the top is not an opening bracket, and either the top has higher precedence, or the top has equal precedence and the incoming operator is left associative.
  5. Push the incoming operator. This happens immediately after the popping in the previous step finishes.
  6. When the scan finishes, pop every remaining operator to the output. Top down. If an opening bracket appears here, brackets were unbalanced.
  7. The output list is the postfix expression. It contains no brackets, and it can be evaluated left to right with a single value stack.

Operator priority and associativity

Priority — precedence — decides which operator wins when two of them compete for the same operand. Associativity resolves the tie when the priority is equal. These are the standard values used in data structures courses and in this site's converter.

Operator precedence tiers used in infix to postfix conversion↑ BINDS TIGHTER3^Exponentiationassociates right to left2* / %Multiply, divide, moduloassociates left to right1+ -Add, subtractassociates left to right
Higher tiers bind tighter and so leave the stack later. Associativity only matters when two operators sit on the same tier.
Infix to postfix conversion priority table
Priority Operator Name Associativity
3 — highest^ExponentiationRight to left
2*   /   %Multiply, divide, moduloLeft to right
1 — lowest+   -Add, subtractLeft to right
n/a(   )BracketsPushed as a marker; never emitted
A common source of confusion

An opening bracket has the highest priority when it is being pushed — nothing stops it going onto the stack — and the lowest when it is already on the stack, because nothing pops it except its own closing bracket. Some textbooks list this as two separate numbers, "in-stack priority" and "incoming priority". Treating ( as a special case rather than a number is simpler and gives identical results.

All conversion rules in one table

Infix to postfix conversion rules, one row per token type
Token read Condition Action
OperandalwaysAppend to output
(alwaysPush onto stack
)alwaysPop to output until (; discard both brackets
Operatorstack emptyPush
Operatortop is (Push
Operatorhigher priority than topPush
Operatorlower priority than topPop top to output, then re-test
Operatorequal priority, left associativePop top to output, then re-test
Operatorequal priority, right associativePush without popping
End of inputstack non-emptyPop everything to output

Pseudocode

Language independent, and a direct transcription of the rules above. Every implementation on this site — including the C program and the JavaScript running the converter — is this same routine.

shunting-yard.pseudo
function infixToPostfix(expression):
    stack  = empty stack
    output = empty list

    for each token in expression:

        if token is an operand:
            output.append(token)

        else if token == '(':
            stack.push(token)

        else if token == ')':
            while stack is not empty and stack.peek() != '(':
                output.append(stack.pop())
            if stack is empty:
                error "unbalanced: missing ("
            stack.pop()                      // discard the '('

        else if token is an operator:
            while stack is not empty
                  and stack.peek() != '('
                  and ( priority(stack.peek()) > priority(token)
                        or ( priority(stack.peek()) == priority(token)
                             and isLeftAssociative(token) ) ):
                output.append(stack.pop())
            stack.push(token)

        else:
            error "invalid character"

    while stack is not empty:
        top = stack.pop()
        if top == '(':
            error "unbalanced: missing )"
        output.append(top)

    return output

Dry run

Tracing K + L - M * N + (O ^ P) * W / U / V * T + Q — the expression that turns up in half the textbooks. Stack top is on the right.

Dry run of the infix to postfix conversion algorithm
# Token Rule applied Stack Output
1KOperandemptyK
2+Stack empty, push+K
3LOperand+K L
4-Equal priority, left assoc — pop +, push-K L +
5MOperand-K L + M
6*Higher priority, push- *K L + M
7NOperand- *K L + M N
8+Pop *, pop -, push+K L + M N * -
9(Push marker+ (K L + M N * -
10OOperand+ (K L + M N * - O
11^Top is (, push+ ( ^K L + M N * - O
12POperand+ ( ^K L + M N * - O P
13)Pop ^, discard (+K L + M N * - O P ^
14*Higher than +, push+ *K L + M N * - O P ^
15WOperand+ *K L + M N * - O P ^ W
16/Equal to *, left assoc — pop, push+ /K L + M N * - O P ^ W *
17UOperand+ /K L + M N * - O P ^ W * U
18/Equal, pop /, push+ /… W * U /
19VOperand+ /… W * U / V
20*Equal, pop /, push+ *… W * U / V /
21TOperand+ *… W * U / V / T
22+Pop *, pop +, push+… V / T * +
23QOperand+… V / T * + Q
24endPop +empty… V / T * + Q +

The complete postfix result is:

Postfix K L + M N * - O P ^ W * U / V / T * + Q +

Rows 18–22 are abbreviated with for width; open this expression in the converter to see every cell in full.

The equal-precedence trap

This is the single most common bug. When the incoming operator has the same priority as the one on top of the stack, most people push. That is wrong for every left-associative operator.

What happens if you push instead of popping on equal precedence
Infix Correct postfix Wrong (pushed on equal) Means
A - B - CA B - C -A B C - -A - (B - C) — different value
A / B / CA B / C /A B C / /A / (B / C) — different value
A + B + CA B + C +A B C + +Same value, but still the wrong tree

Addition hides the bug because it is associative in arithmetic — the answer comes out the same. Subtraction and division expose it immediately, which is why exam questions almost always use them.

Left associativity groups from the left; right associativity groups from the rightLEFT TO RIGHT — + - * / %2nd1sta-b-cgroups as(a - b) - cpostfixa b - c -RIGHT TO LEFT — ^ ONLY2nd1sta^b^cgroups asa ^ (b ^ c)postfixa b c ^ ^
With equal precedence, associativity decides which pair binds first — and that changes the answer. This single difference is why the popping condition needs its associativity test.

Right associativity and ^

Exponentiation is the one operator that groups right to left: a ^ b ^ c means a ^ (b ^ c). So when a second ^ arrives and finds a ^ on the stack, it must not pop it.

a ^ b ^ c

  a       output: a
  ^       stack empty, push          stack: ^
  b       output: a b
  ^       equal priority BUT right associative → push
                                     stack: ^ ^
  c       output: a b c
  end     pop both                   output: a b c ^ ^

  correct:  a b c ^ ^      =  a ^ (b ^ c)
  wrong:    a b ^ c ^      =  (a ^ b) ^ c

In the pseudocode above this is handled by the single clause and isLeftAssociative(token) on the equal-priority test. Remove it and exponentiation breaks; hard-code it to true and everything else breaks.

Time and space complexity

Complexity of the infix to postfix conversion algorithm
Measure Cost Reason
Time O(n) Each token is read once, pushed at most once and popped at most once. The inner while loop looks quadratic but is amortised: it can only pop what an earlier iteration pushed.
Auxiliary space O(n) The stack, in the worst case of deep nesting such as ((((a)))) or a long ^ chain.
Output space O(n) Postfix has the same operands and operators as the infix input, minus the brackets, so it is never longer.
Passes over input 1 A single left-to-right scan; no backtracking and no lookahead.

Invalid input and edge cases

An algorithm that only works on well-formed input is half an algorithm. These are the cases worth handling explicitly, and how each one is detected.

Detecting invalid infix expressions during conversion
Input Problem Detected when
A + B)Extra closing bracketPopping for ) empties the stack without finding (
(A + BUnclosed bracketA ( is still on the stack at the final flush
A + * BTwo operators in a rowTrack whether the next token must be an operand; * arrives when one is expected
A BMissing operatorAn operand arrives when an operator is expected
A +Trailing operatorInput ends while an operand is still expected
()Empty brackets) arrives immediately after (
2(3 + 4)Implied multiplication( arrives when an operator is expected

A single boolean — "the next token must be an operand" — flipped by each token type catches every row except the bracket cases, which the stack itself catches. The converter on this site uses exactly that check, which is why it explains what is wrong instead of silently producing nonsense.

Adapting it for prefix

The same routine produces prefix (Polish notation) with three changes wrapped around it:

  1. Reverse the infix expression, swapping every ( for ) and vice versa.
  2. Run the algorithm, but flip the associativity test — pop only on strictly higher priority for left-associative operators, and on equal priority for right-associative ones.
  3. Reverse the result. That is the prefix expression.

So A + B * C becomes + A * B C. The converter on the homepage shows the prefix form alongside the postfix one for every expression you enter.

Frequently asked questions

What is the algorithm to convert infix to postfix?

Dijkstra's shunting-yard algorithm. Scan the infix expression left to right using an operator stack: append operands to the output, push opening brackets, pop to the output until the matching bracket on a closing bracket, and for each operator pop while the stack top has higher precedence — or equal precedence and the incoming operator is left associative — before pushing it. At the end, pop the whole stack to the output.

Which operator has the highest priority in infix to postfix conversion?

Exponentiation ^ is highest, then multiplication, division and modulo * / %, then addition and subtraction + -. Brackets are not given a priority number in this treatment — an opening bracket is always pushed and is only removed by its matching closing bracket.

What is the time complexity of infix to postfix conversion?

O(n) time and O(n) auxiliary space for n tokens. Every token is read once and every operator is pushed and popped at most once, so the inner popping loop is amortised constant despite looking like a nested loop.

Why is it called the shunting-yard algorithm?

Edsger Dijkstra named it after a railway shunting yard. The input expression is a train of carriages arriving on one track; operators are shunted onto a siding (the stack) and released back onto the main line (the output) in a different order, which is precisely what the algorithm does with operators.

Do brackets appear in the postfix output?

No. Brackets exist only to override precedence in infix notation, and postfix has no precedence to override — the order of symbols alone determines evaluation order. Opening brackets are pushed as markers and discarded when their closing bracket arrives.

What is the difference between the infix to postfix and infix to prefix algorithms?

The core loop is identical. For prefix you reverse the input first (swapping the brackets), flip the associativity comparison so left-associative operators pop only on strictly higher precedence, and reverse the output at the end.

How do you handle unary minus?

Give it its own token — commonly written u- or ~ — with precedence above ^ and right associativity, so it pops nothing and binds to the operand on its right. Simpler implementations, including this site's converter, instead fold the sign into the numeric literal that follows it and ask you to rewrite -x as (0 - x).