DSA Tutorial
🔍

Expression Evaluation

Why Expression Evaluation Uses Stacks

An arithmetic expression has two fundamental challenges: operator precedence (* before +) and associativity (a - b - c means (a - b) - c, left to right). Parentheses add a third layer: explicit grouping that overrides precedence.

A stack resolves all three naturally:

LIFO order mirrors the nesting structure of expressions.
The most recently opened sub-expression must be resolved first —
exactly what parentheses demand, and exactly what a stack provides.

Three notations:
  Infix:   A + B * C   (human-readable, requires precedence rules)
  Postfix: A B C * +   (no parentheses needed, easy to evaluate)
  Prefix:  + A * B C   (no parentheses needed, recursive structure)

Postfix and prefix are unambiguous — no precedence rules required.
Evaluation of both requires only one stack pass.

Expression Notation Reference

Expression: 3 + 4 × 2 - 1

Infix:   3 + 4 * 2 - 1        (operators between operands)
Postfix: 3 4 2 * + 1 -        (operators after operands — RPN)
Prefix:  - + 3 * 4 2 1        (operators before operands — Polish)

Evaluation:
  Postfix: read left-to-right, stack operands, apply operator to top two
  Prefix:  read right-to-left, stack operands, apply operator to top two

Key insight: postfix/prefix encode precedence in their token ordering.
No parentheses or precedence rules needed during evaluation.

Part 1: Evaluate Postfix (Reverse Polish Notation)

Problem: Given a postfix expression as a token list, evaluate it and return the result.

Rule:

  • Token is a number → push onto stack
  • Token is an operator → pop two operands (b then a), apply a OP b, push result
CRITICAL: Pop order matters for subtraction and division.
  The top of the stack is the SECOND operand (pushed last).
  b = stack.pop()   ← right operand
  a = stack.pop()   ← left operand
  result = a OP b
Java
1import java.util.Deque; 2import java.util.ArrayDeque; 3 4public class PostfixEvaluation { 5 6 public static int evalRPN(String[] tokens) { 7 Deque<Integer> stack = new ArrayDeque<>(); 8 9 for (String token : tokens) { 10 switch (token) { 11 case "+": { 12 int b = stack.pop(), a = stack.pop(); 13 stack.push(a + b); 14 break; 15 } 16 case "-": { 17 int b = stack.pop(), a = stack.pop(); 18 stack.push(a - b); // a - b, not b - a 19 break; 20 } 21 case "*": { 22 int b = stack.pop(), a = stack.pop(); 23 stack.push(a * b); 24 break; 25 } 26 case "/": { 27 int b = stack.pop(), a = stack.pop(); 28 stack.push(a / b); // truncates toward zero (Java int division) 29 break; 30 } 31 default: 32 stack.push(Integer.parseInt(token)); // Operand → push 33 } 34 } 35 36 return stack.pop(); // Single remaining value is the result 37 } 38 39 public static void main(String[] args) { 40 // 3 + 4 * 2 = 11 41 System.out.println(evalRPN(new String[]{"3","4","2","*","+"})); // 11 42 // (3 + 4) * 2 = 14 43 System.out.println(evalRPN(new String[]{"3","4","+","2","*"})); // 14 44 // 5 + ((1 + 2) * 4) - 3 = 14 45 System.out.println(evalRPN(new String[]{"5","1","2","+","4","*","+","3","-"})); // 14 46 // 10 / 3 = 3 (truncates toward zero) 47 System.out.println(evalRPN(new String[]{"10","3","/"})); // 3 48 // -4 * 2 = -8 49 System.out.println(evalRPN(new String[]{"-4","2","*"})); // -8 50 } 51}
Output:
11
14
14
3
-8

Dry Run: 5 1 2 + 4 * + 3 -

stack=[]

token '5':  push 5        stack=[5]
token '1':  push 1        stack=[5,1]
token '2':  push 2        stack=[5,1,2]
token '+':  b=2, a=1  → 1+2=3,  push 3   stack=[5,3]
token '4':  push 4        stack=[5,3,4]
token '*':  b=4, a=3  → 3*4=12, push 12  stack=[5,12]
token '+':  b=12,a=5  → 5+12=17,push 17  stack=[17]
token '3':  push 3        stack=[17,3]
token '-':  b=3, a=17 → 17-3=14,push 14  stack=[14]

Result: 14 ✓

Why b is popped FIRST:
  The expression a - b has b on top (it was pushed after a).
  Popping b first then a gives us the correct operand order.
  Getting this wrong turns "17 - 3" into "3 - 17 = -14".

Part 2: Infix to Postfix Conversion (Shunting-Yard Algorithm)

Problem: Convert a human-readable infix expression to postfix notation.

Dijkstra's Shunting-Yard algorithm:

  • Operand → append to output
  • ( → push to operator stack
  • ) → pop and output until ( is found; discard the (
  • Operator → pop and output all operators of ≥ precedence (left-associative), then push
  • End → pop all remaining operators to output
Operator precedence table:
  ^  (exponentiation) → 3, right-associative
  *  /                → 2, left-associative
  +  -                → 1, left-associative
Java
1import java.util.Deque; 2import java.util.ArrayDeque; 3 4public class InfixToPostfix { 5 6 private static int precedence(char op) { 7 if (op == '^') return 3; 8 if (op == '*' || op == '/') return 2; 9 if (op == '+' || op == '-') return 1; 10 return -1; 11 } 12 13 private static boolean isRightAssociative(char op) { 14 return op == '^'; 15 } 16 17 public static String infixToPostfix(String infix) { 18 Deque<Character> opStack = new ArrayDeque<>(); 19 StringBuilder output = new StringBuilder(); 20 21 for (int i = 0; i < infix.length(); i++) { 22 char c = infix.charAt(i); 23 if (c == ' ') continue; 24 25 if (Character.isLetterOrDigit(c)) { 26 // Multi-digit number support 27 output.append(c); 28 // Check if next char is also a digit 29 if (i + 1 < infix.length() && Character.isDigit(infix.charAt(i + 1))) { 30 continue; // Will be appended next iteration 31 } 32 output.append(' '); 33 34 } else if (c == '(') { 35 opStack.push(c); 36 37 } else if (c == ')') { 38 // Pop to output until matching '(' 39 while (!opStack.isEmpty() && opStack.peek() != '(') { 40 output.append(opStack.pop()).append(' '); 41 } 42 opStack.pop(); // Discard '(' 43 44 } else { 45 // Operator: pop higher/equal precedence (respecting associativity) 46 while (!opStack.isEmpty() 47 && opStack.peek() != '(' 48 && (precedence(opStack.peek()) > precedence(c) 49 || (precedence(opStack.peek()) == precedence(c) 50 && !isRightAssociative(c)))) { 51 output.append(opStack.pop()).append(' '); 52 } 53 opStack.push(c); 54 } 55 } 56 57 while (!opStack.isEmpty()) { 58 output.append(opStack.pop()).append(' '); 59 } 60 61 return output.toString().trim(); 62 } 63 64 public static void main(String[] args) { 65 System.out.println(infixToPostfix("A+B*C")); // A B C * + 66 System.out.println(infixToPostfix("(A+B)*C")); // A B + C * 67 System.out.println(infixToPostfix("A+B*C-D/E")); // A B C * + D E / - 68 System.out.println(infixToPostfix("A^B^C")); // A B C ^ ^ (right assoc) 69 System.out.println(infixToPostfix("(A+B)*(C-D)/(E+F)")); // A B + C D - * E F + / 70 } 71}
Output:
A B C * +
A B + C *
A B C * + D E / -
A B C ^ ^
A B + C D - * E F + /

Dry Run: A+B*C-D/E

opStack=[], output=[]

'A':  operand   → output=[A]
'+':  prec(+)=1, stack empty → push  opStack=[+]
'B':  operand   → output=[A,B]
'*':  prec(*)=2 > prec(+)=1  → no pop → push   opStack=[+,*]
'C':  operand   → output=[A,B,C]
'-':  prec(-)=1
       prec(*)=2 ≥ 1 → pop * → output=[A,B,C,*]  opStack=[+]
       prec(+)=1 ≥ 1 → pop + → output=[A,B,C,*,+] opStack=[]
       push -                                       opStack=[-]
'D':  operand   → output=[A,B,C,*,+,D]
'/':  prec(/)=2 > prec(-)=1 → no pop → push  opStack=[-,/]
'E':  operand   → output=[A,B,C,*,+,D,E]

End: pop remaining stack:
  pop /  → output=[A,B,C,*,+,D,E,/]   opStack=[-]
  pop -  → output=[A,B,C,*,+,D,E,/,-] opStack=[]

Result: A B C * + D E / -

Verify: A + B*C - D/E
  B*C first (prec), then A+result, then D/E, then subtract.
  Postfix encodes this order exactly without parentheses.

Part 3: Infix to Prefix Conversion

Problem: Convert infix to prefix notation (Polish Notation). Operators precede their operands.

Algorithm:

  1. Reverse the infix expression
  2. Swap ( and ) in the reversed string
  3. Apply infix-to-postfix on the modified string
  4. Reverse the result
Why this works:
  Prefix is the mirror image of postfix.
  Reversing + swapping brackets converts the problem to a postfix problem.
  Reversing the result gives prefix.
Java
1public class InfixToPrefix { 2 3 private static int precedence(char op) { 4 if (op == '^') return 3; 5 if (op == '*' || op == '/') return 2; 6 if (op == '+' || op == '-') return 1; 7 return -1; 8 } 9 10 private static String toPostfixForPrefix(String s) { 11 java.util.Deque<Character> opStack = new java.util.ArrayDeque<>(); 12 StringBuilder output = new StringBuilder(); 13 14 for (char c : s.toCharArray()) { 15 if (c == ' ') continue; 16 17 if (Character.isLetterOrDigit(c)) { 18 output.append(c).append(' '); 19 20 } else if (c == '(') { // Already swapped from ')' 21 opStack.push(c); 22 23 } else if (c == ')') { // Already swapped from '(' 24 while (!opStack.isEmpty() && opStack.peek() != '(') { 25 output.append(opStack.pop()).append(' '); 26 } 27 if (!opStack.isEmpty()) opStack.pop(); 28 29 } else { 30 // For prefix: right-associative treatment (pop only strictly greater) 31 while (!opStack.isEmpty() 32 && opStack.peek() != '(' 33 && precedence(opStack.peek()) > precedence(c)) { 34 output.append(opStack.pop()).append(' '); 35 } 36 opStack.push(c); 37 } 38 } 39 40 while (!opStack.isEmpty()) { 41 output.append(opStack.pop()).append(' '); 42 } 43 return output.toString().trim(); 44 } 45 46 public static String infixToPrefix(String infix) { 47 // Step 1: Reverse the expression 48 String reversed = new StringBuilder(infix).reverse().toString(); 49 50 // Step 2: Swap brackets 51 StringBuilder swapped = new StringBuilder(); 52 for (char c : reversed.toCharArray()) { 53 if (c == '(') swapped.append(')'); 54 else if (c == ')') swapped.append('('); 55 else swapped.append(c); 56 } 57 58 // Step 3: Apply modified postfix conversion 59 String postfix = toPostfixForPrefix(swapped.toString()); 60 61 // Step 4: Reverse the result 62 return new StringBuilder(postfix).reverse().toString().trim(); 63 } 64 65 public static void main(String[] args) { 66 System.out.println(infixToPrefix("A+B*C")); // + A * B C 67 System.out.println(infixToPrefix("(A+B)*C")); // * + A B C 68 System.out.println(infixToPrefix("A+B*C-D/E")); // - + A * B C / D E 69 System.out.println(infixToPrefix("(A+B)*(C-D)")); // * + A B - C D 70 } 71}
Output:
+ A * B C
* + A B C
- + A * B C / D E
* + A B - C D

Part 4: Evaluate Prefix Expression

Problem: Given a prefix expression as a token list, evaluate it.

Rule: Read tokens right to left (opposite of postfix).

  • Token is a number → push onto stack
  • Token is an operator → pop two operands (a then b), apply a OP b, push result
Why right to left?
  In prefix, the operator comes before its operands.
  Reading right to left gives operands before their operator —
  exactly the same as postfix read left to right.
  The first pop gives the LEFT operand (a), second pop gives RIGHT (b).
Java
1import java.util.Deque; 2import java.util.ArrayDeque; 3 4public class PrefixEvaluation { 5 6 public static int evalPrefix(String[] tokens) { 7 Deque<Integer> stack = new ArrayDeque<>(); 8 9 // Process right to left 10 for (int i = tokens.length - 1; i >= 0; i--) { 11 String token = tokens[i]; 12 13 if (token.equals("+") || token.equals("-") 14 || token.equals("*") || token.equals("/")) { 15 int a = stack.pop(); // LEFT operand (first popped when reading RTL) 16 int b = stack.pop(); // RIGHT operand 17 switch (token) { 18 case "+": stack.push(a + b); break; 19 case "-": stack.push(a - b); break; 20 case "*": stack.push(a * b); break; 21 case "/": stack.push(a / b); break; 22 } 23 } else { 24 stack.push(Integer.parseInt(token)); 25 } 26 } 27 28 return stack.pop(); 29 } 30 31 public static void main(String[] args) { 32 // + 3 * 4 2 = 3 + 4*2 = 11 33 System.out.println(evalPrefix(new String[]{"+","3","*","4","2"})); // 11 34 // * + 3 4 2 = (3+4)*2 = 14 35 System.out.println(evalPrefix(new String[]{"*","+","3","4","2"})); // 14 36 // - + 5 * 1 4 3 = 5 + 1*4 - 3 = 6 37 System.out.println(evalPrefix(new String[]{"-","+","5","*","1","4","3"})); // 6 38 // - + A * B C / D E — symbolic (can't eval letters, but shows structure) 39 } 40}
Output:
11
14
6

Dry Run: Prefix * + 3 4 2 (= (3+4)×2 = 14)

tokens = ["*", "+", "3", "4", "2"]
Read right to left:

i=4: '2'  → push 2    stack=[2]
i=3: '4'  → push 4    stack=[2,4]
i=2: '3'  → push 3    stack=[2,4,3]
i=1: '+'  → a=pop()=3 (left), b=pop()=4 (right) → 3+4=7, push 7
            stack=[2,7]
i=0: '*'  → a=pop()=7 (left), b=pop()=2 (right) → 7*2=14, push 14
            stack=[14]

Result: 14 ✓

Why a is the LEFT operand here:
  Reading right-to-left, the rightmost operand is pushed first.
  When the operator is reached, the left operand is on top
  (it was the closest one to the right of the operator).
  So first pop = left, second pop = right.

Part 5: Evaluate Arithmetic Expression String

Problem: Given an infix arithmetic string like "3 + 4 * 2" or "(1+(4+5+2)-3)+(6+8)", evaluate it directly without first converting to postfix.

Algorithm (two-stack method):

  • One stack for operands (numbers)
  • One stack for operators
  • When an operator with higher or equal precedence is on the operator stack, evaluate it immediately
Java
1import java.util.Deque; 2import java.util.ArrayDeque; 3 4public class EvaluateInfixString { 5 6 private static int precedence(char op) { 7 if (op == '+' || op == '-') return 1; 8 if (op == '*' || op == '/') return 2; 9 return 0; 10 } 11 12 private static int applyOp(int a, int b, char op) { 13 switch (op) { 14 case '+': return a + b; 15 case '-': return a - b; 16 case '*': return a * b; 17 case '/': return a / b; 18 } 19 return 0; 20 } 21 22 public static int evaluate(String s) { 23 Deque<Integer> nums = new ArrayDeque<>(); 24 Deque<Character> ops = new ArrayDeque<>(); 25 int i = 0; 26 27 while (i < s.length()) { 28 char c = s.charAt(i); 29 30 if (c == ' ') { i++; continue; } 31 32 if (Character.isDigit(c)) { 33 // Parse multi-digit number 34 int num = 0; 35 while (i < s.length() && Character.isDigit(s.charAt(i))) { 36 num = num * 10 + (s.charAt(i) - '0'); 37 i++; 38 } 39 nums.push(num); 40 continue; // i already advanced 41 42 } else if (c == '(') { 43 ops.push(c); 44 45 } else if (c == ')') { 46 // Evaluate everything inside the parentheses 47 while (!ops.isEmpty() && ops.peek() != '(') { 48 int b = nums.pop(), a = nums.pop(); 49 nums.push(applyOp(a, b, ops.pop())); 50 } 51 ops.pop(); // Discard '(' 52 53 } else if (c == '+' || c == '-' || c == '*' || c == '/') { 54 // Evaluate operators with >= precedence before pushing 55 while (!ops.isEmpty() && ops.peek() != '(' 56 && precedence(ops.peek()) >= precedence(c)) { 57 int b = nums.pop(), a = nums.pop(); 58 nums.push(applyOp(a, b, ops.pop())); 59 } 60 ops.push(c); 61 } 62 63 i++; 64 } 65 66 // Evaluate remaining operators 67 while (!ops.isEmpty()) { 68 int b = nums.pop(), a = nums.pop(); 69 nums.push(applyOp(a, b, ops.pop())); 70 } 71 72 return nums.pop(); 73 } 74 75 public static void main(String[] args) { 76 System.out.println(evaluate("3 + 4 * 2")); // 11 77 System.out.println(evaluate("(3 + 4) * 2")); // 14 78 System.out.println(evaluate("(1+(4+5+2)-3)+(6+8)")); // 23 79 System.out.println(evaluate("100 * 2 + 12")); // 212 80 System.out.println(evaluate("100 * ( 2 + 12 )")); // 1400 81 System.out.println(evaluate("100 * ( 2 + 12 ) / 14")); // 100 82 } 83}
Output:
11
14
23
100

Dry Run: "3 + 4 * 2" → 11

nums=[], ops=[]

'3':   digit → num=3, push    nums=[3]         ops=[]
' ':   skip
'+':   prec(+)=1, ops empty → push +           nums=[3]    ops=[+]
' ':   skip
'4':   digit → num=4, push    nums=[3,4]        ops=[+]
' ':   skip
'*':   prec(*)=2 > prec(+)=1 → no pop → push * nums=[3,4]  ops=[+,*]
' ':   skip
'2':   digit → num=2, push    nums=[3,4,2]      ops=[+,*]

End of string — drain ops:
  pop *: b=2, a=4 → 4*2=8, push 8  nums=[3,8]  ops=[+]
  pop +: b=8, a=3 → 3+8=11,push 11 nums=[11]   ops=[]

Result: 11 ✓

Notation Comparison and Complexity

                  Infix          Postfix         Prefix
Example:         A + B * C      A B C * +       + A * B C
Parentheses:     Required       Never needed    Never needed
Human readability: High         Low             Very low
Evaluation:      Complex        O(n) one stack  O(n) one stack RTL
Conversion from  —              O(n) Shunting   O(n) via postfix
infix:
Recursive struct: Natural       Iterative easy  Mirrors recursion
OperationTimeSpaceNotes
Evaluate postfixO(n)O(n)One stack, single pass
Evaluate prefixO(n)O(n)One stack, right-to-left
Infix → postfixO(n)O(n)Shunting-yard algorithm
Infix → prefixO(n)O(n)Reverse + shunting + reverse
Evaluate infix stringO(n)O(n)Two stacks, single pass

Common Mistakes

Wrong operand pop order in postfix. When applying an operator, the right operand is on top (it was pushed last). Always pop b first (right), then a (left). For a - b, getting this wrong returns b - a — produces wrong answers for subtraction and division, correct for addition and multiplication (both commutative).

Using int division instead of truncation toward zero. Python's int(a / b) truncates toward zero. Python's floor division a // b truncates toward negative infinity. For eval_rpn(["-7","2","/"]), the LeetCode spec expects -3 (truncate toward zero), not -4 (floor). Always use int(a / b) or Math.trunc(a / b) for postfix evaluation problems.

Not handling multi-digit numbers in infix evaluation. Reading one character at a time breaks on "123 + 4". Always collect digits in a loop until the next character is not a digit before pushing the number to the operand stack.

Skipping the '(' guard in the operator pop loop. The operator pop loop must stop at '(' — never pop a ( as an operator. Omitting ops.peek() != '(' causes parenthesised sub-expressions to be evaluated prematurely.

Not clearing redo stack — actually, not draining the operator stack after input ends. After reading all tokens, the operator stack may still contain operators. Always drain it completely: while (!ops.isEmpty()) { evaluate top two nums with top op }.

Interview Questions

Q: Why does postfix evaluation never need parentheses?

Postfix encodes operator precedence in the ordering of tokens. In A B C * +, the * appears after both its operands B and C and before +. When evaluated left to right with a stack, * is always applied to B and C before + gets to use the result — exactly what A + B * C means. The token order makes precedence unambiguous without any special rules or symbols.

Q: Why must the infix-to-prefix algorithm reverse the string before applying the postfix algorithm?

Prefix is the mirror image of postfix — the operator precedes its operands instead of following them. Reversing the input string converts each prefix sub-expression into a postfix sub-expression (the operands appear before the operator when read left to right). Swapping brackets maintains the correct grouping in the reversed order. Reversing the output then mirrors postfix back to prefix. This avoids writing a separate algorithm from scratch.

Q: What is the difference between the one-stack postfix evaluator and the two-stack infix evaluator?

Postfix is already linearised — no precedence resolution is needed at evaluation time. One stack for operands suffices: push numbers, apply operators to the top two immediately. Infix requires deferring operators until higher-precedence operations complete. Two stacks — one for operands, one for operators — let the algorithm peek at the top operator's precedence before deciding whether to apply it or defer it. The two-stack approach is essentially inline Shunting-Yard + evaluation in one pass.

FAQs

Can these algorithms handle unary minus (negative numbers like -4)?

Simple implementations handle negative number tokens ("-4" as a pre-tokenised string) but not unary minus in expressions like "-4 + 2" as a character sequence. For unary minus in infix strings, handle c == '-' at the start of the string or immediately after ( as a sign, not an operator. Push 0 first, then the - operator, so "-4" becomes 0 - 4. Alternatively, pre-tokenise the string to identify sign vs subtraction.

What is associativity and why does it matter for ^?

Associativity determines how equal-precedence operators group. Left-associative: a - b - c = (a - b) - c. Right-associative: a ^ b ^ c = a ^ (b ^ c). For left-associative operators, pop when the stack top has equal or greater precedence. For right-associative (^), only pop when the stack top has strictly greater precedence — equal precedence means "group right first." Without this distinction, 2^3^2 would be computed as (2^3)^2 = 64 instead of 2^(3^2) = 512.

When would you use prefix notation in practice?

Prefix notation (Polish notation) is used in LISP and Scheme programming languages where all expressions are written as (operator operands...). It is also used in some calculators, compilers for AST serialisation, and certain logic systems. Its advantage is that recursive evaluation is extremely natural — an evaluator can be written as a single recursive function that reads the operator, evaluates two sub-expressions, and applies the operator. Postfix is used in stack-based virtual machines (JVM bytecode, PostScript, Forth).

Quick Quiz

Question 1: Evaluate postfix 8 2 / 3 -. What is the result?

  • A) 1
  • B) 4
  • C) -1
  • D) 7

Answer: A) 1. 8 2 / → b=2, a=8 → 8/2=4, push 4. 4 3 - → b=3, a=4 → 4-3=1. Result: 1.

Question 2: In postfix evaluation, for operator -, which value is popped first?

  • A) The left operand (pushed first, deeper in stack)
  • B) The right operand (pushed last, on top)
  • C) Either — subtraction is commutative
  • D) The smaller value

Answer: B) The right operand is popped first. The right operand was pushed last so it sits on top. Pop b (right) first, then a (left). Compute a - b. Reversing gives b - a — wrong for all non-symmetric operations.

Question 3: Converting A^B^C to postfix should give A B C ^ ^ (right-associative). What would happen if ^ were treated as left-associative?

  • A) The result would be the same — associativity doesn't affect postfix
  • B) It would produce A B ^ C ^, evaluating as (A^B)^C instead of A^(B^C)
  • C) It would produce a syntax error
  • D) It would produce A B C ^ +

Answer: B) A B ^ C ^, evaluating as (A^B)^C. Left-associative processing pops ^ from the stack when a new ^ arrives (equal precedence triggers a pop). This produces A B ^ C ^ which evaluates (A^B)^C. Right-associative processing does NOT pop on equal precedence, leaving both ^ operators on the stack to be popped in the correct right-to-left order, giving A B C ^ ^ which evaluates A^(B^C).

Question 4: The two-stack infix evaluator needs to evaluate "(3 + 4) * 2". At what point does the + operation get applied?

  • A) When + is pushed to the ops stack
  • B) When * is encountered and has higher precedence than +
  • C) When ) is encountered, which triggers evaluation until ( is found
  • D) At the end when the ops stack is drained

Answer: C) When ) is encountered. The ) triggers a loop that pops and evaluates operators until ( is found. At that point, + pops, applies to 3 and 4 giving 7, which is pushed to nums. Then ( is discarded. Now * applies to 7 and 2 giving 14.

Summary

Expression evaluation with stacks covers five distinct problems, each with a specific algorithm:

Evaluate postfix (RPN): Single stack, left to right. Numbers → push. Operator → pop b (right), pop a (left), apply a OP b, push result. O(n) time, O(n) space.

Infix to postfix (Shunting-Yard): Operator stack tracks deferred operators. Operands go directly to output. ( pushes to stack. ) pops to output until (. Operators pop equal/higher-precedence before pushing. End → drain stack. Handles left and right associativity.

Infix to prefix: Reverse input, swap brackets, apply modified postfix conversion (pop only strictly greater, not equal), reverse the result tokens.

Evaluate prefix: Single stack, right to left. Numbers → push. Operator → pop a (left), pop b (right), apply a OP b, push result.

Evaluate infix string directly: Two stacks — operands and operators. Same operator-precedence logic as Shunting-Yard, but immediately apply whenever a lower-precedence operator arrives or ) is encountered.

Critical correctness points: pop order determines operand assignment for non-commutative operators; right-associativity requires > not >= in the pop condition; multi-digit numbers require a digit-collection loop; the ops stack must be fully drained after all input is consumed.

Expression Evaluation