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.
| Infix | Postfix | Prefix |
|---|---|---|
x + y | x y + | + x y |
x + y * z | x y z * + | + x * y z |
(x + y) * z | x y + z * | * + x y z |
a + b * (c - d) / e | a 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
+ preorder (prefix) : + a * b c
/ \ inorder (infix) : a + b * c
a * postorder (postfix) : a b c * +
/ \
b cPrecedence 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.
| Operator | Precedence | Associativity | Example |
|---|---|---|---|
^ | 3 (highest) | right to left | 2 ^ 1 ^ 2 = 2 ^ (1 ^ 2) = 2 ^ 1 = 2 |
*, / | 2 | left to right | 12 / 2 * 3 = (12 / 2) * 3 = 18 |
+, - | 1 (lowest) | left to right | 10 + 2 - 3 = (10 + 2) - 3 = 9 |
Precedence:
10 + 20 * 2→10 + 40→50, not60.Left associativity:
10 - 2 - 3→(10 - 2) - 3→5, not11.Right associativity:
2 ^ 3 ^ 2→2 ^ (3 ^ 2)→2 ^ 9→512, not64.
Converting by hand: fully parenthesise, then move operators
Parenthesise fully
Add brackets that make precedence and associativity explicit:
x + y * z→(x + (y * z)).Convert innermost first
Replace each
(a op b)bya b op(postfix) orop a b(prefix), working outwards:(x + (y z *))→x y z * +.Drop the brackets
They are no longer needed — the result is the postfix (or prefix) form.
| Infix | Fully parenthesised | Postfix | Prefix |
|---|---|---|---|
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)
Operand
Append it to the output.
(Push it.
)Pop and output operators until the
(; pop the(and discard it (brackets never appear in postfix).Operator
xWhile the stack top is an operator with higher precedence than
x, or equal precedence andxis left-associative, pop it to the output. Then pushx.End of input
Pop and output everything left on the stack.
| Symbol | Action | Stack (bottom → top) | Output |
|---|---|---|---|
a | operand → output | a | |
+ | stack empty → push | + | a |
b | operand → output | + | a b |
* | higher than + → push | + * | a b |
( | push | + * ( | a b |
c | operand → output | + * ( | a b c |
- | top is ( → push | + * ( - | a b c |
d | operand → 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 - * |
e | operand → output | + / | a b c d - * e |
| end | pop everything | a b c d - * e / + |
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))#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";
}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));
}
}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));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
Operand
Push it.
Operator
right = pop(),left = pop(), pushleft op right.End
The single remaining value is the result.
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]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))#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";
}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));
}
}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));10 2 * 3 + = 23
2 3 1 * + 9 - = -4
100 20 5 / - = 96
2 3 ^ 1 - = 7Time 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.
Reverse
Reverse the infix string and swap every
(with):(a+b)*c→c*(b+a).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+*.Reverse again
Reverse the output:
cba+*→*+abc.
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))#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";
}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));
}
}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));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 → --abcTime O(n) · Space O(n)
Evaluating a prefix expression
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))#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";
}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));
}
}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));+ 10 * 2 3 = 16
- + 7 * 4 5 2 = 25
/ 100 - 20 15 = 20Time O(n) · Space O(n)
The three notations side by side
| Infix | Postfix | Prefix | |
|---|---|---|---|
| Operator position | between operands | after operands | before operands |
| Needs brackets / precedence | yes | no | no |
| Evaluation scan | needs two stacks or a parser | left → right, one stack | right → left, one stack |
| First pop on an operator is | — | the right operand | the left operand |
| Tree traversal | inorder | postorder | preorder |
| Used by | people, source code | JVM / CPython bytecode, PostScript, RPN calculators | Lisp, 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?