Topics › Stacks and Queues › Prefix, Infix, PostFix Conversion Problems
Prefix to Infix Conversion
Java source ↗ Edit this article ↗
Jump to code:
Problem
A prefix (Polish) expression writes each binary operator before its two operands: +ab means a+b, and *+ab-cd means (a+b)*(c-d). Like postfix, it needs no parentheses. Convert a prefix expression to a fully parenthesised infix expression, in which every operator application is wrapped in its own pair of parentheses so that the original structure is unambiguous.
Operands are single characters (letters or digits); operators are +, -, *, /, ^. The input has no spaces and is assumed well formed; the code throws IllegalArgumentException when an operator lacks operands or operands are left over.
Constraints: 1 ≤ n ≤ 10^5 characters.
Example 1
prefix = *-A/BC-/AKL
infix = ((A-(B/C))*((A/K)-L))
The leading * multiplies two sub-expressions: -A/BC, which is A-(B/C), and -/AKL, which is (A/K)-L.
Example 2
prefix = +12
infix = (1+2)
Both approaches use one helper:
static boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
}
Approach 1: Recursion from the left
Intuition. A prefix expression is either a single operand, or an operator followed by two complete prefix expressions, the left operand first and the right operand second. That is a recursive definition, so a recursive parser follows it exactly: read one character; if it is an operand, that is the whole expression; if it is an operator, parse the left expression, then the right expression, and wrap them as (left op right). The recursion does not need to know where the left operand ends, because the recursive call consumes exactly the characters that belong to it and leaves the cursor at the start of the right operand.
Algorithm.
- Keep a shared cursor
pos, starting at 0. parse: if the cursor is past the end, the expression is incomplete, throw. Readcat the cursor and advance.- If
cis an operand, return it as a one-character string. - Otherwise
cis an operator:left = parse(),right = parse(), return"(" + left + c + right + ")". - After the top-level
parse, the cursor must be at the end of the input; otherwise there are leftover characters.
static String recursive(String prefix) {
int[] pos = {0}; // cursor shared by all recursive calls
String result = parse(prefix, pos);
if (pos[0] != prefix.length()) throw new IllegalArgumentException("leftover tokens from index " + pos[0]);
return result;
}
String result = parse(prefix, pos);
if (pos[0] != prefix.length()) throw new IllegalArgumentException("leftover tokens from index " + pos[0]);
return result;
}
Complexity. Time O(n) recursive calls, one per character. The string concatenations copy each sub-result once per enclosing level, so the total character copying is O(n × depth): O(n log n) for balanced trees and O(n^2) for a degenerate chain such as ----abcde. Space O(depth) for the call stack, which is O(n) in the worst case, plus the output.
The cursor is passed as a one-element int[] so that the recursive calls can advance it; Java passes arguments by value, so a plain int parameter would not be updated for the caller.
Approach 2: Optimal, a stack scanned from the right
Intuition. Reading prefix left to right, an operator arrives before its operands, so we cannot combine anything yet. Reading right to left, by the time we meet an operator both of its operands have already been seen, and they are the two most recently completed sub-expressions. “Most recently completed” is exactly what a stack gives us. So scan from the end: push each operand; on an operator pop two sub-expressions, combine them, and push the result. One sub-expression remains at the end: the answer.
The order of the two pops matters. Scanning right to left, the operand that is closer to the operator in the original string, that is the left operand, is pushed later, so it is popped first.
Algorithm.
- For
ifromn - 1down to0, letcbe the character ati. - If
cis an operand, push the one-character string. - If
cis an operator: if the stack has fewer than two entries, throw.left = pop(),right = pop(), push"(" + left + c + right + ")". - At the end the stack must contain exactly one string; return it.
static String optimal(String prefix) {
Deque<String> stack = new ArrayDeque<>();
for (int i = prefix.length() - 1; i >= 0; i--) {
char c = prefix.charAt(i);
if (isOperator(c)) {
if (stack.size() < 2) throw new IllegalArgumentException("operator '" + c + "' at " + i + " lacks operands");
String left = stack.pop(); // nearer to the operator = left operand
String right = stack.pop();
stack.push("(" + left + c + right + ")");
} else {
stack.push(String.valueOf(c));
}
}
if (stack.size() != 1) throw new IllegalArgumentException("malformed prefix expression");
return stack.pop();
}
Dry run on example 1, *-A/BC-/AKL, scanning from the right:
| i | c | action | stack (bottom → top) |
|---|---|---|---|
| 10 | L | push | L |
| 9 | K | push | L, K |
| 8 | A | push | L, K, A |
| 7 | / | pop A, pop K | L, (A/K) |
| 6 | - | pop (A/K), pop L | ((A/K)-L) |
| 5 | C | push | ((A/K)-L), C |
| 4 | B | push | ((A/K)-L), C, B |
| 3 | / | pop B, pop C | ((A/K)-L), (B/C) |
| 2 | A | push | ((A/K)-L), (B/C), A |
| 1 | - | pop A, pop (B/C) | ((A/K)-L), (A-(B/C)) |
| 0 | * | pop (A-(B/C)), pop ((A/K)-L) | ((A-(B/C))*((A/K)-L)) |
Complexity. Time O(n) stack operations, with the same O(n × depth) bound on character copying as approach 1, because the same strings are built. Space O(n) for the stack of partial strings.
The two approaches build the same tree, once top-down and once bottom-up; the recursive version is a depth-first walk of the expression tree, and the stack version is the same walk with the recursion made explicit. The test file runs both on identical inputs and expects identical output, including the asymmetric chains -a-b-cd → (a-(b-(c-d))) and ---abcd → (((a-b)-c)-d).
Edge cases and pitfalls
- Operand order. Popping
rightbeforeleftturns-abinto(b-a). For commutative operators the mistake is invisible, so test with-or/. - Scanning direction. Scanning prefix left to right with a stack does not work directly; either reverse the scan (approach 2) or recurse (approach 1). For postfix the roles flip: a left-to-right scan works, and the first pop is the right operand.
- Lone operand.
ais a valid prefix expression and converts toawithout parentheses. Wrapping only happens around operator applications. - Malformed input.
+aruns out of operands;ableaves two sub-expressions;+ab+has a trailing operator. Both implementations detect each of these: the recursive one by the cursor check, the stack one by the stack size. - Deep recursion. A chain like
----...-abwith10^5operators nests10^5recursive calls, which can overflow the default Java thread stack. The iterative stack version has no such limit. - Quadratic string building. For very long, deeply nested inputs the repeated concatenation dominates. Building an explicit expression tree and emitting it with one
StringBuilderin an in-order walk brings the output cost down toO(n).
Related problems
- Infix to Postfix Conversion and Infix to Prefix Conversion go the other way, using an operator stack and precedence rules.
- Postfix to Infix Conversion is this algorithm scanned left to right with the pops swapped.
- Prefix to Postfix Conversion and Postfix to Prefix Conversion use the same stack of partial strings but emit no parentheses.
- Balanced Paranthesis is a good warm-up for stack-based parsing.