Skip to content
  • Infix, Postfix, Prefix · DSA Advanced

Infix, Postfix, Prefix

LearnViaNotes @learnvianotes· 23 min read

Humans write x + y; machines prefer x y + or + x y, because those forms need no brackets, no precedence table and no associativity rules — a single left-to-right pass with a stack evaluates them. Converting between the three notations is the classic stack algorithm.

InfixPostfixPrefix
x + yx y ++ x y
x + y * zx y z * ++ x * y z
(x + y) * zx y + z ** + x y z
a + b * (c - d) / ea b c d - * e / ++ a / * b - c d e
a ^ b ^ c (right-assoc)a b c ^ ^^ a ^ b c

Why postfix and prefix are useful

The expression tree of a + b * c and its three traversals
   +                    preorder  (prefix)  : + a * b c
 /   \                  inorder   (infix)   : a + b * c
a     *                 postorder (postfix) : a b c * +
     / \
    b   c

Precedence and associativity

Infix is ambiguous without two extra rules. Precedence decides which operator binds tighter when they differ; associativity decides the order when they are equal.

OperatorPrecedenceAssociativityExample
^3 (highest)right to left2 ^ 1 ^ 2 = 2 ^ (1 ^ 2) = 2 ^ 1 = 2
*, /2left to right12 / 2 * 3 = (12 / 2) * 3 = 18
+, -1 (lowest)left to right10 + 2 - 3 = (10 + 2) - 3 = 9
  • Precedence: 10 + 20 * 2 → 10 + 40 → 50, not 60.

  • Left associativity: 10 - 2 - 3 → (10 - 2) - 3 → 5, not 11.

  • Right associativity: 2 ^ 3 ^ 2 → 2 ^ (3 ^ 2) → 2 ^ 9 → 512, not 64.

Converting by hand: fully parenthesise, then move operators

  1. Parenthesise fully

    Add brackets that make precedence and associativity explicit: x + y * z → (x + (y * z)).

  2. Convert innermost first

    Replace each (a op b) by a b op (postfix) or op a b (prefix), working outwards: (x + (y z *)) → x y z * +.

  3. Drop the brackets

    They are no longer needed — the result is the postfix (or prefix) form.

InfixFully parenthesisedPostfixPrefix
x + y * z(x + (y * z))x y z * ++ x * y z
(x + y) * z((x + y) * z)x y + z ** + x y z
a - b - c((a - b) - c)a b - c -- - a b c
a ^ b ^ c(a ^ (b ^ c))a b c ^ ^^ a ^ b c

Infix to postfix (shunting-yard algorithm)

  1. Operand

    Append it to the output.

  2. (

    Push it.

  3. )

    Pop and output operators until the (; pop the ( and discard it (brackets never appear in postfix).

  4. Operator x

    While the stack top is an operator with higher precedence than x, or equal precedence and x is left-associative, pop it to the output. Then push x.

  5. End of input

    Pop and output everything left on the stack.

SymbolActionStack (bottom → top)Output
aoperand → outputa
+stack empty → push+a
boperand → output+a b
*higher than + → push+ *a b
(push+ * (a b
coperand → output+ * (a b c
-top is ( → push+ * ( -a b c
doperand → output+ * ( -a b c d
)pop to (: output -, discard (+ *a b c d -
/equal to *, left-assoc → pop *; + is lower → stop; push /+ /a b c d - *
eoperand → output+ /a b c d - * e
endpop everythinga b c d - * e / +
Python
PREC = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}
RIGHT_ASSOC = {'^'}

def infix_to_postfix(expr):
    out, st = [], []
    for ch in expr:
        if ch == ' ':
            continue
        if ch.isalnum():                       # operand: straight to the output
            out.append(ch)
        elif ch == '(':
            st.append(ch)
        elif ch == ')':
            while st[-1] != '(':               # pop until the matching '('
                out.append(st.pop())
            st.pop()                           # discard the '(' itself
        else:                                  # operator
            while (st and st[-1] != '(' and
                   (PREC[st[-1]] > PREC[ch] or
                    (PREC[st[-1]] == PREC[ch] and ch not in RIGHT_ASSOC))):
                out.append(st.pop())           # higher (or equal, left-assoc) comes out first
            st.append(ch)
    while st:
        out.append(st.pop())
    return ''.join(out)

for e in ["a+b*c", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"]:
    print(e, "→", infix_to_postfix(e))
C++
#include <cctype>
#include <iostream>
#include <stack>
#include <string>
using namespace std;

int prec(char op) {
    if (op == '^') return 3;
    if (op == '*' || op == '/') return 2;
    if (op == '+' || op == '-') return 1;
    return 0;
}
bool rightAssoc(char op) { return op == '^'; }

string infixToPostfix(const string& expr) {
    string out;
    stack<char> st;
    for (char ch : expr) {
        if (ch == ' ') continue;
        if (isalnum(static_cast<unsigned char>(ch))) {
            out += ch;                                   // operand: straight to the output
        } else if (ch == '(') {
            st.push(ch);
        } else if (ch == ')') {
            while (st.top() != '(') { out += st.top(); st.pop(); }   // pop until the matching '('
            st.pop();                                    // discard the '(' itself
        } else {
            while (!st.empty() && st.top() != '(' &&
                   (prec(st.top()) > prec(ch) || (prec(st.top()) == prec(ch) && !rightAssoc(ch)))) {
                out += st.top(); st.pop();               // higher (or equal, left-assoc) comes out first
            }
            st.push(ch);
        }
    }
    while (!st.empty()) { out += st.top(); st.pop(); }
    return out;
}

int main() {
    for (string e : {"a+b*c", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"})
        cout << e << " → " << infixToPostfix(e) << "\n";
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    static int prec(char op) {
        if (op == '^') return 3;
        if (op == '*' || op == '/') return 2;
        if (op == '+' || op == '-') return 1;
        return 0;
    }
    static boolean rightAssoc(char op) { return op == '^'; }

    static String infixToPostfix(String expr) {
        StringBuilder out = new StringBuilder();
        Deque<Character> st = new ArrayDeque<>();
        for (char ch : expr.toCharArray()) {
            if (ch == ' ') continue;
            if (Character.isLetterOrDigit(ch)) {
                out.append(ch);                              // operand: straight to the output
            } else if (ch == '(') {
                st.push(ch);
            } else if (ch == ')') {
                while (st.peek() != '(') out.append(st.pop());   // pop until the matching '('
                st.pop();                                    // discard the '(' itself
            } else {
                while (!st.isEmpty() && st.peek() != '(' &&
                       (prec(st.peek()) > prec(ch) || (prec(st.peek()) == prec(ch) && !rightAssoc(ch))))
                    out.append(st.pop());                    // higher (or equal, left-assoc) comes out first
                st.push(ch);
            }
        }
        while (!st.isEmpty()) out.append(st.pop());
        return out.toString();
    }

    public static void main(String[] args) {
        for (String e : new String[]{"a+b*c", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"})
            System.out.println(e + " → " + infixToPostfix(e));
    }
}
JavaScript
const PREC = { '+': 1, '-': 1, '*': 2, '/': 2, '^': 3 };
const RIGHT_ASSOC = new Set(['^']);
const isOperand = ch => /[A-Za-z0-9]/.test(ch);

function infixToPostfix(expr) {
    const out = [], st = [];
    const top = () => st[st.length - 1];
    for (const ch of expr) {
        if (ch === ' ') continue;
        if (isOperand(ch)) {
            out.push(ch);                                   // operand: straight to the output
        } else if (ch === '(') {
            st.push(ch);
        } else if (ch === ')') {
            while (top() !== '(') out.push(st.pop());       // pop until the matching '('
            st.pop();                                       // discard the '(' itself
        } else {
            while (st.length && top() !== '(' &&
                   (PREC[top()] > PREC[ch] || (PREC[top()] === PREC[ch] && !RIGHT_ASSOC.has(ch))))
                out.push(st.pop());                         // higher (or equal, left-assoc) comes out first
            st.push(ch);
        }
    }
    while (st.length) out.push(st.pop());
    return out.join('');
}

for (const e of ["a+b*c", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"])
    console.log(e, "→", infixToPostfix(e));
Output
a+b*c → abc*+
(a+b)*c → ab+c*
a+b*(c-d)/e → abcd-*e/+
a^b^c → abc^^
a-b-c → ab-c-

Time O(n) · Space O(n) for the operator stack

Evaluating a postfix expression

  1. Operand

    Push it.

  2. Operator

    right = pop(), left = pop(), push left op right.

  3. End

    The single remaining value is the result.

Each operator replaces the top two values by one
tokens:  10   2   *   3   +
stack:  [10] [10 2] [20] [20 3] [23]        2 3 1 * + 9 -  →  [2] [2 3] [2 3 1] [2 3] [5] [5 9] [-4]
Python
def apply(op, a, b):
    if op == '+': return a + b
    if op == '-': return a - b
    if op == '*': return a * b
    if op == '/': return a // b           # integer division (operands kept positive here)
    if op == '^': return a ** b
    raise ValueError(op)

def eval_postfix(expr):
    st = []
    for tok in expr.split():
        if tok.lstrip('-').isdigit():
            st.append(int(tok))
        else:
            right = st.pop()              # first pop is the RIGHT operand
            left = st.pop()
            st.append(apply(tok, left, right))
    return st[-1]                         # exactly one value remains

for e in ["10 2 * 3 +", "2 3 1 * + 9 -", "100 20 5 / -", "2 3 ^ 1 -"]:
    print(e, "=", eval_postfix(e))
C++
#include <iostream>
#include <sstream>
#include <stack>
#include <string>
using namespace std;

long long ipow(long long a, long long b) { long long r = 1; while (b-- > 0) r *= a; return r; }

long long apply(const string& op, long long a, long long b) {
    if (op == "+") return a + b;
    if (op == "-") return a - b;
    if (op == "*") return a * b;
    if (op == "/") return a / b;         // integer division (operands kept positive here)
    return ipow(a, b);                   // "^"
}

long long evalPostfix(const string& expr) {
    stack<long long> st;
    istringstream in(expr);
    string tok;
    while (in >> tok) {
        if (isdigit(static_cast<unsigned char>(tok.back()))) {
            st.push(stoll(tok));
        } else {
            long long right = st.top(); st.pop();   // first pop is the RIGHT operand
            long long left = st.top(); st.pop();
            st.push(apply(tok, left, right));
        }
    }
    return st.top();                                // exactly one value remains
}

int main() {
    for (string e : {"10 2 * 3 +", "2 3 1 * + 9 -", "100 20 5 / -", "2 3 ^ 1 -"})
        cout << e << " = " << evalPostfix(e) << "\n";
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    static long ipow(long a, long b) { long r = 1; while (b-- > 0) r *= a; return r; }

    static long apply(String op, long a, long b) {
        switch (op) {
            case "+": return a + b;
            case "-": return a - b;
            case "*": return a * b;
            case "/": return a / b;          // integer division (operands kept positive here)
            default:  return ipow(a, b);     // "^"
        }
    }

    static long evalPostfix(String expr) {
        Deque<Long> st = new ArrayDeque<>();
        for (String tok : expr.split(" ")) {
            if (Character.isDigit(tok.charAt(tok.length() - 1))) {
                st.push(Long.parseLong(tok));
            } else {
                long right = st.pop();           // first pop is the RIGHT operand
                long left = st.pop();
                st.push(apply(tok, left, right));
            }
        }
        return st.peek();                        // exactly one value remains
    }

    public static void main(String[] args) {
        for (String e : new String[]{"10 2 * 3 +", "2 3 1 * + 9 -", "100 20 5 / -", "2 3 ^ 1 -"})
            System.out.println(e + " = " + evalPostfix(e));
    }
}
JavaScript
function apply(op, a, b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return Math.trunc(a / b);     // integer division (operands kept positive here)
        case '^': return a ** b;
    }
    throw new Error(op);
}

function evalPostfix(expr) {
    const st = [];
    for (const tok of expr.split(' ')) {
        if (/\d$/.test(tok)) {
            st.push(Number(tok));
        } else {
            const right = st.pop();              // first pop is the RIGHT operand
            const left = st.pop();
            st.push(apply(tok, left, right));
        }
    }
    return st[st.length - 1];                    // exactly one value remains
}

for (const e of ["10 2 * 3 +", "2 3 1 * + 9 -", "100 20 5 / -", "2 3 ^ 1 -"])
    console.log(e, "=", evalPostfix(e));
Output
10 2 * 3 + = 23
2 3 1 * + 9 - = -4
100 20 5 / - = 96
2 3 ^ 1 - = 7

Time O(n) · Space O(n) for the operand stack

Infix to prefix

By hand: fully parenthesise and convert from the innermost bracket outwards, writing each operator before its operands — x + (y * z) → x + (* y z) → + x * y z. With a stack there are two routes; the reversal trick reuses the postfix code almost unchanged.

  1. Reverse

    Reverse the infix string and swap every ( with ): (a+b)*c → c*(b+a).

  2. Convert

    Run infix-to-postfix on it, popping on strictly higher precedence (or equal precedence when the operator is right-associative): c*(b+a) → cba+*.

  3. Reverse again

    Reverse the output: cba+* → *+abc.

Python
PREC = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}
RIGHT_ASSOC = {'^'}

def infix_to_prefix(expr):
    rev = ''.join(')' if c == '(' else '(' if c == ')' else c for c in reversed(expr))
    out, st = [], []
    for ch in rev:
        if ch == ' ':
            continue
        if ch.isalnum():
            out.append(ch)
        elif ch == '(':
            st.append(ch)
        elif ch == ')':
            while st[-1] != '(':
                out.append(st.pop())
            st.pop()
        else:
            while (st and st[-1] != '(' and
                   (PREC[st[-1]] > PREC[ch] or
                    (PREC[st[-1]] == PREC[ch] and ch in RIGHT_ASSOC))):   # associativity flipped
                out.append(st.pop())
            st.append(ch)
    while st:
        out.append(st.pop())
    return ''.join(reversed(out))                                       # undo the reversal

for e in ["x+y*z", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"]:
    print(e, "→", infix_to_prefix(e))
C++
#include <algorithm>
#include <cctype>
#include <iostream>
#include <stack>
#include <string>
using namespace std;

int prec(char op) {
    if (op == '^') return 3;
    if (op == '*' || op == '/') return 2;
    if (op == '+' || op == '-') return 1;
    return 0;
}
bool rightAssoc(char op) { return op == '^'; }

string infixToPrefix(string expr) {
    reverse(expr.begin(), expr.end());
    for (char& c : expr) c = c == '(' ? ')' : c == ')' ? '(' : c;   // swap the brackets
    string out;
    stack<char> st;
    for (char ch : expr) {
        if (ch == ' ') continue;
        if (isalnum(static_cast<unsigned char>(ch))) {
            out += ch;
        } else if (ch == '(') {
            st.push(ch);
        } else if (ch == ')') {
            while (st.top() != '(') { out += st.top(); st.pop(); }
            st.pop();
        } else {
            while (!st.empty() && st.top() != '(' &&
                   (prec(st.top()) > prec(ch) || (prec(st.top()) == prec(ch) && rightAssoc(ch)))) {
                out += st.top(); st.pop();                        // associativity flipped
            }
            st.push(ch);
        }
    }
    while (!st.empty()) { out += st.top(); st.pop(); }
    reverse(out.begin(), out.end());                              // undo the reversal
    return out;
}

int main() {
    for (string e : {"x+y*z", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"})
        cout << e << " → " << infixToPrefix(e) << "\n";
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    static int prec(char op) {
        if (op == '^') return 3;
        if (op == '*' || op == '/') return 2;
        if (op == '+' || op == '-') return 1;
        return 0;
    }
    static boolean rightAssoc(char op) { return op == '^'; }

    static String infixToPrefix(String expr) {
        StringBuilder rev = new StringBuilder();
        for (int i = expr.length() - 1; i >= 0; i--) {              // reverse and swap the brackets
            char c = expr.charAt(i);
            rev.append(c == '(' ? ')' : c == ')' ? '(' : c);
        }
        StringBuilder out = new StringBuilder();
        Deque<Character> st = new ArrayDeque<>();
        for (char ch : rev.toString().toCharArray()) {
            if (ch == ' ') continue;
            if (Character.isLetterOrDigit(ch)) {
                out.append(ch);
            } else if (ch == '(') {
                st.push(ch);
            } else if (ch == ')') {
                while (st.peek() != '(') out.append(st.pop());
                st.pop();
            } else {
                while (!st.isEmpty() && st.peek() != '(' &&
                       (prec(st.peek()) > prec(ch) || (prec(st.peek()) == prec(ch) && rightAssoc(ch))))
                    out.append(st.pop());                        // associativity flipped
                st.push(ch);
            }
        }
        while (!st.isEmpty()) out.append(st.pop());
        return out.reverse().toString();                         // undo the reversal
    }

    public static void main(String[] args) {
        for (String e : new String[]{"x+y*z", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"})
            System.out.println(e + " → " + infixToPrefix(e));
    }
}
JavaScript
const PREC = { '+': 1, '-': 1, '*': 2, '/': 2, '^': 3 };
const RIGHT_ASSOC = new Set(['^']);
const isOperand = ch => /[A-Za-z0-9]/.test(ch);

function infixToPrefix(expr) {
    const rev = [...expr].reverse().map(c => c === '(' ? ')' : c === ')' ? '(' : c);   // swap the brackets
    const out = [], st = [];
    const top = () => st[st.length - 1];
    for (const ch of rev) {
        if (ch === ' ') continue;
        if (isOperand(ch)) {
            out.push(ch);
        } else if (ch === '(') {
            st.push(ch);
        } else if (ch === ')') {
            while (top() !== '(') out.push(st.pop());
            st.pop();
        } else {
            while (st.length && top() !== '(' &&
                   (PREC[top()] > PREC[ch] || (PREC[top()] === PREC[ch] && RIGHT_ASSOC.has(ch))))
                out.push(st.pop());                              // associativity flipped
            st.push(ch);
        }
    }
    while (st.length) out.push(st.pop());
    return out.reverse().join('');                               // undo the reversal
}

for (const e of ["x+y*z", "(a+b)*c", "a+b*(c-d)/e", "a^b^c", "a-b-c"])
    console.log(e, "→", infixToPrefix(e));
Output
x+y*z → +x*yz
(a+b)*c → *+abc
a+b*(c-d)/e → +a/*b-cde
a^b^c → ^a^bc
a-b-c → --abc

Time O(n) · Space O(n)

Evaluating a prefix expression

Python
def apply(op, a, b):
    if op == '+': return a + b
    if op == '-': return a - b
    if op == '*': return a * b
    if op == '/': return a // b
    raise ValueError(op)

def eval_prefix(expr):
    st = []
    for tok in reversed(expr.split()):      # right to left
        if tok.isdigit():
            st.append(int(tok))
        else:
            left = st.pop()                 # first pop is the LEFT operand this time
            right = st.pop()
            st.append(apply(tok, left, right))
    return st[-1]

for e in ["+ 10 * 2 3", "- + 7 * 4 5 2", "/ 100 - 20 15"]:
    print(e, "=", eval_prefix(e))
C++
#include <cctype>
#include <iostream>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
using namespace std;

long long apply(const string& op, long long a, long long b) {
    if (op == "+") return a + b;
    if (op == "-") return a - b;
    if (op == "*") return a * b;
    return a / b;
}

long long evalPrefix(const string& expr) {
    vector<string> toks;
    istringstream in(expr);
    for (string t; in >> t;) toks.push_back(t);
    stack<long long> st;
    for (int i = (int)toks.size() - 1; i >= 0; i--) {         // right to left
        if (isdigit(static_cast<unsigned char>(toks[i].back()))) {
            st.push(stoll(toks[i]));
        } else {
            long long left = st.top(); st.pop();              // first pop is the LEFT operand this time
            long long right = st.top(); st.pop();
            st.push(apply(toks[i], left, right));
        }
    }
    return st.top();
}

int main() {
    for (string e : {"+ 10 * 2 3", "- + 7 * 4 5 2", "/ 100 - 20 15"})
        cout << e << " = " << evalPrefix(e) << "\n";
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    static long apply(String op, long a, long b) {
        switch (op) {
            case "+": return a + b;
            case "-": return a - b;
            case "*": return a * b;
            default:  return a / b;
        }
    }

    static long evalPrefix(String expr) {
        String[] toks = expr.split(" ");
        Deque<Long> st = new ArrayDeque<>();
        for (int i = toks.length - 1; i >= 0; i--) {              // right to left
            if (Character.isDigit(toks[i].charAt(toks[i].length() - 1))) {
                st.push(Long.parseLong(toks[i]));
            } else {
                long left = st.pop();                             // first pop is the LEFT operand this time
                long right = st.pop();
                st.push(apply(toks[i], left, right));
            }
        }
        return st.peek();
    }

    public static void main(String[] args) {
        for (String e : new String[]{"+ 10 * 2 3", "- + 7 * 4 5 2", "/ 100 - 20 15"})
            System.out.println(e + " = " + evalPrefix(e));
    }
}
JavaScript
function apply(op, a, b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return Math.trunc(a / b);
    }
    throw new Error(op);
}

function evalPrefix(expr) {
    const toks = expr.split(' ');
    const st = [];
    for (let i = toks.length - 1; i >= 0; i--) {                  // right to left
        if (/\d$/.test(toks[i])) {
            st.push(Number(toks[i]));
        } else {
            const left = st.pop();                                // first pop is the LEFT operand this time
            const right = st.pop();
            st.push(apply(toks[i], left, right));
        }
    }
    return st[st.length - 1];
}

for (const e of ["+ 10 * 2 3", "- + 7 * 4 5 2", "/ 100 - 20 15"])
    console.log(e, "=", evalPrefix(e));
Output
+ 10 * 2 3 = 16
- + 7 * 4 5 2 = 25
/ 100 - 20 15 = 20

Time O(n) · Space O(n)

The three notations side by side

InfixPostfixPrefix
Operator positionbetween operandsafter operandsbefore operands
Needs brackets / precedenceyesnono
Evaluation scanneeds two stacks or a parserleft → right, one stackright → left, one stack
First pop on an operator is—the right operandthe left operand
Tree traversalinorderpostorderpreorder
Used bypeople, source codeJVM / CPython bytecode, PostScript, RPN calculatorsLisp, some compilers' IRs

Interview Q&A

Why do compilers and virtual machines prefer postfix?

How does the shunting-yard algorithm handle associativity?

Can you evaluate an infix expression directly, without converting first?

What is wrong with evaluating postfix character by character?

How are the three notations related to the expression tree?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report