Prefix to Postfix Conversion
Description
You are given a string that represents a valid mathematical expression written in prefix notation (also known as Polish notation). In prefix notation, the operator appears before its two operands.
Your task is to convert this prefix expression into its equivalent postfix notation (also known as Reverse Polish notation). In postfix notation, the operator appears after its two operands.
The string contains only lowercase and uppercase English alphabets as operands, and the operators are +, -, *, /, %, and ^.
For example, the prefix expression *+AB-CD means "multiply the sum of A and B by the difference of C and D." In postfix form, this becomes AB+CD-* — the operands come first, followed by their operator.
Examples
Example 1
Input: pre_exp = "*-A/BC-/AKL"
Output: "ABC/-AK/L-*"
Explanation: Breaking down the prefix expression:
*operates on two sub-expressions:-A/BCand-/AKL.-A/BCin postfix: A is the left operand,/BCbecomesBC/in postfix, so the subtraction becomesABC/-.-/AKLin postfix:/AKbecomesAK/in postfix, L is the right operand, so the subtraction becomesAK/L-.- The final
*combines both:ABC/-AK/L-*.
Example 2
Input: pre_exp = "*+AB-CD"
Output: "AB+CD-*"
Explanation: The + operates on A and B → AB+ in postfix. The - operates on C and D → CD- in postfix. The * combines them → AB+CD-*.
Example 3
Input: pre_exp = "+AB"
Output: "AB+"
Explanation: This is the simplest possible prefix expression — one operator with two operands. In postfix notation, the operands come first followed by the operator: AB+.
Constraints
- 3 ≤ |pre_exp| ≤ 100
- The string contains only lowercase and uppercase English alphabets as operands
- Operators are limited to:
+,-,*,/,%,^ - The input is always a valid prefix expression
Editorial
Brute Force - Recursive Descent Parsing
Intuition
Just like prefix notation has a recursive structure (an operator followed by two sub-expressions), we can use recursion to parse and convert it.
The key difference between prefix-to-infix and prefix-to-postfix is simply how we reassemble the pieces:
- In infix:
(left operator right)— operator goes between operands. - In postfix:
left right operator— operator goes after operands.
Imagine reading a recipe written in a strange format: "MIX ADD flour sugar BLEND butter eggs." You need to restructure it as: "flour sugar ADD butter eggs BLEND MIX" — the actions (operators) come at the end of each group.
We maintain a pointer that moves through the prefix string from left to right. At each position:
- If we see a letter (operand), we return it immediately.
- If we see an operator, we recursively obtain its left and right operand strings, then concatenate them as
left + right + operator.
This recursive parsing naturally handles nested sub-expressions because each recursive call consumes exactly one complete sub-expression from the input.
Step-by-Step Explanation
Let's trace through with pre_exp = "*+AB-CD":
Step 1: Call solve() at index 0. Character is *.
*is an operator. We need its left and right operand sub-expressions in postfix form.- Advance idx to 1. Recursively call solve() for the left operand.
- State: idx = 0, char = '*', type = operator
Step 2: Recursive call at index 1. Character is +.
+is an operator. Recursively parse its left operand.- Advance idx to 2.
- State: idx = 1, char = '+', type = operator
Step 3: Recursive call at index 2. Character is A.
Ais an operand. Return "A". Advance idx to 3.- State: idx = 2 → 3, returns = "A"
Step 4: Back in the + call. Left = "A". Parse right operand at index 3. Character is B.
Bis an operand. Return "B". Advance idx to 4.- State: idx = 3 → 4, returns = "B"
Step 5: Back in the + call. Left = "A", Right = "B".
- Postfix combination: left + right + operator = "A" + "B" + "+" = "AB+"
- Return "AB+".
- State: combined = "AB+"
Step 6: Back in the * call. Left = "AB+". Parse right operand at index 4. Character is -.
-is an operator. Recursively parse its operands.- Advance idx to 5.
- State: idx = 4, char = '-', type = operator
Step 7: Recursive call at index 5. Character is C.
Cis an operand. Return "C". Advance idx to 6.- State: idx = 5 → 6, returns = "C"
Step 8: Parse right operand of - at index 6. Character is D.
Dis an operand. Return "D". Advance idx to 7.- State: idx = 6 → 7, returns = "D"
Step 9: Back in the - call. Left = "C", Right = "D".
- Postfix combination: "C" + "D" + "-" = "CD-"
- Return "CD-".
- State: combined = "CD-"
Step 10: Back in the * call. Left = "AB+", Right = "CD-".
- Postfix combination: "AB+" + "CD-" + "" = "AB+CD-"
- Return "AB+CD-*".
Final Result: "AB+CD-*"
Algorithm
- Initialize a global index pointer
idx = 0. - Define a recursive function
solve():
a. Read the character at positionidxand advanceidxby 1.
b. If the character is a letter (operand), return it as a string.
c. If the character is an operator:- Recursively call
solve()to get the left operand string (in postfix). - Recursively call
solve()to get the right operand string (in postfix). - Return
left + right + operator.
- Recursively call
- Call
solve()and return the result.
Code
class Solution {
public:
int idx;
string solve(string &preExp) {
char ch = preExp[idx++];
// If operand (letter), return as string
if (isalpha(ch)) {
return string(1, ch);
}
// Operator: recursively parse left and right
string left = solve(preExp);
string right = solve(preExp);
// Postfix: operands first, then operator
return left + right + ch;
}
string preToPost(string pre_exp) {
idx = 0;
return solve(pre_exp);
}
};class Solution:
def preToPost(self, pre_exp):
self.idx = 0
def solve():
ch = pre_exp[self.idx]
self.idx += 1
# If operand (letter), return it
if ch.isalpha():
return ch
# Operator: recursively parse left and right
left = solve()
right = solve()
# Postfix: operands first, then operator
return left + right + ch
return solve()class Solution {
int idx;
String solve(String preExp) {
char ch = preExp.charAt(idx++);
// If operand (letter), return as string
if (Character.isLetter(ch)) {
return String.valueOf(ch);
}
// Operator: recursively parse left and right
String left = solve(preExp);
String right = solve(preExp);
// Postfix: operands first, then operator
return left + right + ch;
}
String preToPost(String pre_exp) {
idx = 0;
return solve(pre_exp);
}
}Complexity Analysis
Time Complexity: O(N²) in the worst case
The recursive parsing visits each character exactly once, which is O(N). However, at every operator node we perform string concatenation of left + right + operator. In languages like Java and Python, each concatenation creates a new string by copying all characters. For a deeply nested expression (e.g., +A+B+C...), the intermediate strings grow progressively, and the total concatenation work sums to O(1 + 2 + 3 + ... + N/2) = O(N²).
Space Complexity: O(N)
The recursion depth equals the nesting depth of the prefix expression. In the worst case (a completely skewed expression like +A+B+C+D...), the depth is O(N/2) = O(N). Each recursive call uses O(1) stack frame space, giving O(N) total call stack usage.
Why This Approach Is Not Efficient
The recursive approach works correctly but has practical drawbacks:
-
Recursion depth risk: For a deeply nested prefix expression, the recursion depth can reach O(N/2). While the constraint here is small (N ≤ 100), this approach does not scale well. In Python, the default recursion limit is 1,000, and in competitive programming, expressions can be much longer. A stack overflow would crash the program.
-
Function call overhead: Each character triggers a function call with its associated overhead — saving/restoring registers, allocating stack frames, and managing return addresses. For an iterative approach, each character is handled in a simple loop iteration, which is significantly faster in practice.
-
String concatenation cost: At each operator node, a new string is created by copying the left and right operand strings plus the operator. For degenerate expressions, this leads to O(N²) total character copies.
Key insight: By scanning the prefix expression from right to left and using an explicit stack, we can build the postfix expression iteratively. Operands encountered during the right-to-left scan are pushed onto the stack, and operators combine them in postfix order — all without recursion.
Optimal Approach - Stack-Based Right-to-Left Traversal
Intuition
The same right-to-left scanning technique that works for prefix-to-infix conversion works here too, with one small change in how we combine operands.
When scanning a prefix expression from right to left:
- Operands appear first (they are at the rightmost positions).
- Operators appear later and need two operands each.
This naturally maps to a stack: push operands, and when you see an operator, pop two operands and combine them.
The only difference from infix conversion is the combination step:
- Infix:
"(" + left + operator + right + ")" - Postfix:
left + right + operator
Think of it like assembling instructions for a calculator that processes numbers left-to-right and applies operations as they appear. The stack holds partial results (sub-expressions in postfix form), and each operator pops two partial results, merges them with the operator at the end, and pushes the merged result back.
The algorithm is elegant and efficient: one pass through the string, one stack, no recursion.
Step-by-Step Explanation
Let's trace through with pre_exp = "*+AB-CD":
We scan from right to left: D, C, -, B, A, +, *
Step 1: Read 'D' (index 6). It is a letter — an operand.
- Push "D" onto the stack.
- Stack: ["D"]
Step 2: Read 'C' (index 5). It is a letter — an operand.
- Push "C" onto the stack.
- Stack: ["D", "C"]
Step 3: Read '-' (index 4). It is an operator.
- Pop two operands: first pop = "C" (left operand), second pop = "D" (right operand).
- Combine in postfix order: "C" + "D" + "-" = "CD-".
- Push "CD-" back onto the stack.
- Stack: ["CD-"]
Step 4: Read 'B' (index 3). It is an operand.
- Push "B" onto the stack.
- Stack: ["CD-", "B"]
Step 5: Read 'A' (index 2). It is an operand.
- Push "A" onto the stack.
- Stack: ["CD-", "B", "A"]
Step 6: Read '+' (index 1). It is an operator.
- Pop two operands: first pop = "A" (left), second pop = "B" (right).
- Combine in postfix: "A" + "B" + "+" = "AB+".
- Push "AB+" back.
- Stack: ["CD-", "AB+"]
Step 7: Read '*' (index 0). It is an operator.
- Pop two operands: first pop = "AB+" (left), second pop = "CD-" (right).
- Combine in postfix: "AB+" + "CD-" + "" = "AB+CD-".
- Push result.
- Stack: ["AB+CD-*"]
Result: The stack has one element: "AB+CD-*".
Prefix to Postfix — Stack-Based Right-to-Left Conversion — Watch how scanning from right to left and combining operands in postfix order (left + right + operator) converts the prefix expression to postfix in a single pass.
Algorithm
- Create an empty stack of strings.
- Scan the prefix expression from right to left (from the last character to the first):
- If the current character is a letter (operand): push it onto the stack as a single-character string.
- If the current character is an operator (+, -, *, /, %, ^):
a. Pop the top element from the stack — this is the left operand.
b. Pop the next top element — this is the right operand.
c. Form the string:left + right + operator(postfix order — no parentheses needed).
d. Push this combined string back onto the stack.
- After processing all characters, the stack contains exactly one element — the postfix expression. Return it.
Code
#include <stack>
#include <string>
using namespace std;
class Solution {
public:
string preToPost(string pre_exp) {
stack<string> st;
int n = pre_exp.size();
// Scan from right to left
for (int i = n - 1; i >= 0; i--) {
char ch = pre_exp[i];
if (isalpha(ch)) {
// Operand: push as a single-character string
st.push(string(1, ch));
} else {
// Operator: pop two operands, combine in postfix order
string left = st.top(); st.pop();
string right = st.top(); st.pop();
st.push(left + right + ch);
}
}
return st.top();
}
};class Solution:
def preToPost(self, pre_exp):
stack = []
# Scan from right to left
for i in range(len(pre_exp) - 1, -1, -1):
ch = pre_exp[i]
if ch.isalpha():
# Operand: push directly
stack.append(ch)
else:
# Operator: pop two operands, combine in postfix order
left = stack.pop()
right = stack.pop()
stack.append(left + right + ch)
return stack[-1]import java.util.Stack;
class Solution {
String preToPost(String pre_exp) {
Stack<String> stack = new Stack<>();
int n = pre_exp.length();
// Scan from right to left
for (int i = n - 1; i >= 0; i--) {
char ch = pre_exp.charAt(i);
if (Character.isLetter(ch)) {
// Operand: push as string
stack.push(String.valueOf(ch));
} else {
// Operator: pop two operands, combine in postfix order
String left = stack.pop();
String right = stack.pop();
stack.push(left + right + ch);
}
}
return stack.peek();
}
}Complexity Analysis
Time Complexity: O(N)
We traverse the string exactly once from right to left, processing each of the N characters with constant-time stack operations (push and pop). Each character is involved in at most one push and one pop. The loop itself is O(N).
Note: Similar to prefix-to-infix, string concatenation at operator steps can lead to O(N²) total character copies in the worst case. However, with N ≤ 100 in this problem, this is negligible. For the core algorithm (excluding string building), the work is strictly O(N).
Space Complexity: O(N)
The stack holds at most O(N) elements at any point (in the worst case, all operands are pushed before any operator is encountered). The intermediate strings stored on the stack together occupy O(N) total space. No recursion is used, so there is no call stack overhead.