Facebook Pixel

439. Ternary Expression Parser 🔒

Problem Description

This problem asks you to evaluate a string that represents nested ternary expressions and return the final result.

A ternary expression follows the format: condition ? value_if_true : value_if_false

The input string expression contains:

  • Digits 0-9 (single digits only)
  • Characters 'T' (representing true) and 'F' (representing false)
  • Operators '?' and ':' that form the ternary structure

Key rules for evaluation:

  1. The expression is always valid and properly formatted
  2. Ternary expressions are right-associative, meaning they group from right to left
  3. The final result will always be a single character: either a digit ('0'-'9'), 'T', or 'F'

For example:

  • "T?2:3" evaluates to "2" (since T is true, take the first value after ?)
  • "F?1:T?4:5" evaluates to "4" (F is false, so take the part after :, which is "T?4:5", and T is true, so take "4")
  • "T?T?F:5:3" evaluates to "F" (T is true, so evaluate "T?F:5", and since T is true, the result is "F")

The solution uses a stack-based approach, processing the expression from right to left. When encountering a '?', it pops values from the stack and decides which one to keep based on whether the condition character is 'T' or 'F'.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that ternary expressions naturally form a tree-like structure where each ? represents a decision point. Since these expressions are right-associative, we need to evaluate from the innermost (rightmost) expressions first.

Why process from right to left? Consider the expression "T?2:F?3:4". If we try to process left to right, when we see T?2:, we don't know what comes after the : - it could be a simple value or another nested expression. However, if we process right to left, we can build up the result step by step from the leaves of the expression tree.

The stack naturally handles this evaluation order. As we traverse the string backwards:

  • Regular characters (digits, 'T', 'F') are potential values that get pushed onto the stack
  • The : separator is just a delimiter we can skip
  • The ? operator triggers an evaluation - it means we've collected both branches of a ternary expression

When we encounter a ?, we know that:

  1. The character immediately before ? is the condition
  2. The top of the stack contains the "false" branch (what comes after :)
  3. The second item on the stack contains the "true" branch (what comes between ? and :)

By popping these two values and keeping only the appropriate one based on the condition, we effectively evaluate one ternary expression and replace it with its result. This process continues until we've evaluated the entire expression, leaving just the final answer on the stack.

This approach elegantly handles nested expressions because inner expressions get evaluated first (being rightmost), and their results become the operands for outer expressions.

Solution Approach

The solution uses a stack-based approach with right-to-left traversal to evaluate the ternary expression.

Implementation Details:

  1. Initialize Data Structures:

    • Create an empty stack stk to store intermediate values
    • Use a boolean flag cond to track when we need to evaluate a ternary operation
  2. Traverse the Expression Backwards:

    for c in expression[::-1]:

    We iterate through each character from right to left using Python's slice notation [::-1].

  3. Process Each Character:

    • Skip colons: When we encounter ':', we simply continue to the next iteration as it's just a separator
    if c == ':':
        continue
    • Mark ternary evaluation: When we see '?', set the cond flag to True, indicating the next character will be a condition
    if c == '?':
        cond = True
    • Handle conditions and values:
      • If cond is True, the current character is a condition ('T' or 'F')
        • If condition is 'T': Pop two values from stack, keep the first one (true branch)
        • If condition is 'F': Pop two values from stack, keep the second one (false branch)
        • Reset cond to False after evaluation
      • If cond is False, the current character is a value - push it onto the stack
    if cond:
        if c == 'T':
            x = stk.pop()  # Get true branch
            stk.pop()      # Discard false branch
            stk.append(x)  # Keep true branch
        else:
            stk.pop()      # Discard true branch (keep false branch on stack)
        cond = False
    else:
        stk.append(c)      # Push value onto stack
  4. Return Result: After processing all characters, the stack contains exactly one element - the final result

    return stk[0]

Example Walkthrough: For expression "T?2:3"

  • Start from right: '3' → push to stack: ['3']
  • Next: ':' → skip
  • Next: '2' → push to stack: ['3', '2']
  • Next: '?' → set cond = True
  • Next: 'T' with cond = True → pop '2', pop '3', push back '2': ['2']
  • Result: '2'

This algorithm has O(n) time complexity and O(n) space complexity, where n is the length of the expression.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the expression "F?1:T?4:5" step by step using the stack-based approach:

Initial Setup:

  • Expression: "F?1:T?4:5"
  • Stack: []
  • cond: False

Processing from right to left:

  1. Character: '5'

    • cond is False, so push '5' onto stack
    • Stack: ['5']
  2. Character: ':'

    • Skip (it's just a separator)
    • Stack: ['5']
  3. Character: '4'

    • cond is False, so push '4' onto stack
    • Stack: ['5', '4']
  4. Character: '?'

    • Set cond = True (next character will be a condition)
    • Stack: ['5', '4']
  5. Character: 'T'

    • cond is True, so this is a condition
    • Since it's 'T' (true):
      • Pop '4' (true branch)
      • Pop '5' (false branch)
      • Push back '4' (we keep the true branch)
    • Reset cond = False
    • Stack: ['4']
  6. Character: ':'

    • Skip
    • Stack: ['4']
  7. Character: '1'

    • cond is False, so push '1' onto stack
    • Stack: ['4', '1']
  8. Character: '?'

    • Set cond = True
    • Stack: ['4', '1']
  9. Character: 'F'

    • cond is True, so this is a condition
    • Since it's 'F' (false):
      • Pop '1' (true branch) and discard it
      • Keep '4' (false branch) on the stack
    • Reset cond = False
    • Stack: ['4']

Final Result: The stack contains ['4'], so the answer is '4'.

This demonstrates how the algorithm evaluates nested expressions from the inside out, with the rightmost ternary expression "T?4:5" being evaluated first to produce '4', which then becomes the false branch of the outer expression "F?1:4".

Solution Implementation

1class Solution:
2    def parseTernary(self, expression: str) -> str:
3        """
4        Parse a ternary expression string and return the evaluated result.
5      
6        The expression follows the format: condition ? true_value : false_value
7        where condition is 'T' (true) or 'F' (false), and values can be 
8        single characters or nested ternary expressions.
9      
10        Args:
11            expression: A valid ternary expression string
12          
13        Returns:
14            The evaluated result as a string (single character)
15        """
16        # Stack to store intermediate results while parsing
17        stack = []
18      
19        # Flag to indicate when we encounter a condition (after '?')
20        is_condition_next = False
21      
22        # Process the expression from right to left
23        for char in reversed(expression):
24            if char == ':':
25                # Skip colons as they're just separators
26                continue
27              
28            if char == '?':
29                # Mark that the next character will be a condition
30                is_condition_next = True
31            else:
32                if is_condition_next:
33                    # This character is a condition ('T' or 'F')
34                    # Pop the last two values from stack (true_value and false_value)
35                    if char == 'T':
36                        # Condition is true: keep the first popped value (true_value)
37                        true_value = stack.pop()
38                        false_value = stack.pop()  # Discard false_value
39                        stack.append(true_value)
40                    else:
41                        # Condition is false: keep the second popped value (false_value)
42                        true_value = stack.pop()  # Discard true_value
43                        false_value = stack.pop()
44                        stack.append(false_value)
45                  
46                    # Reset the condition flag
47                    is_condition_next = False
48                else:
49                    # Regular character (operand): push to stack
50                    stack.append(char)
51      
52        # The final result is the only element left in the stack
53        return stack[0]
54
1class Solution {
2    /**
3     * Parses a ternary expression and returns the evaluated result.
4     * The expression is processed from right to left using a stack.
5     * 
6     * @param expression the ternary expression string to parse
7     * @return the evaluated result as a string
8     */
9    public String parseTernary(String expression) {
10        // Stack to store characters during evaluation
11        Deque<Character> stack = new ArrayDeque<>();
12      
13        // Flag to indicate if we're processing a condition
14        boolean isProcessingCondition = false;
15      
16        // Traverse the expression from right to left
17        for (int i = expression.length() - 1; i >= 0; i--) {
18            char currentChar = expression.charAt(i);
19          
20            // Skip colon separators as they just divide true/false branches
21            if (currentChar == ':') {
22                continue;
23            }
24          
25            // Question mark indicates start of a ternary condition
26            if (currentChar == '?') {
27                isProcessingCondition = true;
28            } else {
29                // Process the condition or push operands to stack
30                if (isProcessingCondition) {
31                    // Evaluate the ternary expression based on condition
32                    if (currentChar == 'T') {
33                        // If condition is true, keep the first value (true branch)
34                        char trueValue = stack.pop();
35                        stack.pop(); // Remove the false branch value
36                        stack.push(trueValue);
37                    } else {
38                        // If condition is false, keep the second value (false branch)
39                        stack.pop(); // Remove the true branch value
40                        // The false branch value remains at top of stack
41                    }
42                    isProcessingCondition = false;
43                } else {
44                    // Push regular characters (operands) to stack
45                    stack.push(currentChar);
46                }
47            }
48        }
49      
50        // The final result is at the top of the stack
51        return String.valueOf(stack.peek());
52    }
53}
54
1class Solution {
2public:
3    string parseTernary(string expression) {
4        // Stack to store intermediate results during evaluation
5        string stack;
6      
7        // Flag to indicate if we're processing a condition (after encountering '?')
8        bool isProcessingCondition = false;
9      
10        // Reverse the expression to process from right to left
11        // This allows us to evaluate nested ternary expressions correctly
12        reverse(expression.begin(), expression.end());
13      
14        // Process each character in the reversed expression
15        for (char& currentChar : expression) {
16            // Skip colon separators as they just delimit true/false branches
17            if (currentChar == ':') {
18                continue;
19            }
20          
21            // Question mark indicates we need to evaluate a condition next
22            if (currentChar == '?') {
23                isProcessingCondition = true;
24            } else {
25                // Process the condition or operand
26                if (isProcessingCondition) {
27                    // Evaluate the ternary expression based on the condition
28                    if (currentChar == 'T') {
29                        // If condition is true, keep the first value (true branch)
30                        char trueValue = stack.back();
31                        stack.pop_back();  // Remove true value
32                        stack.pop_back();  // Remove false value
33                        stack.push_back(trueValue);  // Push back the true value
34                    } else {
35                        // If condition is false, keep the second value (false branch)
36                        stack.pop_back();  // Remove true value, keeping false value
37                    }
38                    isProcessingCondition = false;
39                } else {
40                    // Regular operand - add to stack
41                    stack.push_back(currentChar);
42                }
43            }
44        }
45      
46        // Return the final result as a string
47        return string(1, stack[0]);
48    }
49};
50
1function parseTernary(expression: string): string {
2    // Stack to store intermediate results during evaluation
3    const stack: string[] = [];
4  
5    // Flag to indicate if we're processing a condition (after encountering '?')
6    let isProcessingCondition: boolean = false;
7  
8    // Reverse the expression to process from right to left
9    // This allows us to evaluate nested ternary expressions correctly
10    const reversedExpression: string = expression.split('').reverse().join('');
11  
12    // Process each character in the reversed expression
13    for (const currentChar of reversedExpression) {
14        // Skip colon separators as they just delimit true/false branches
15        if (currentChar === ':') {
16            continue;
17        }
18      
19        // Question mark indicates we need to evaluate a condition next
20        if (currentChar === '?') {
21            isProcessingCondition = true;
22        } else {
23            // Process the condition or operand
24            if (isProcessingCondition) {
25                // Evaluate the ternary expression based on the condition
26                if (currentChar === 'T') {
27                    // If condition is true, keep the first value (true branch)
28                    const trueValue: string = stack[stack.length - 1];
29                    stack.pop();  // Remove true value
30                    stack.pop();  // Remove false value
31                    stack.push(trueValue);  // Push back the true value
32                } else {
33                    // If condition is false, keep the second value (false branch)
34                    stack.pop();  // Remove true value, keeping false value
35                }
36                isProcessingCondition = false;
37            } else {
38                // Regular operand - add to stack
39                stack.push(currentChar);
40            }
41        }
42    }
43  
44    // Return the final result as a string
45    return stack[0];
46}
47

Time and Space Complexity

Time Complexity: O(n) where n is the length of the expression string.

The algorithm iterates through the expression string once in reverse order (from right to left). For each character in the string, it performs constant-time operations:

  • Checking if the character is ':', '?', 'T', or 'F'
  • Stack push and pop operations, which are O(1)

Since we visit each character exactly once and perform O(1) operations for each character, the overall time complexity is O(n).

Space Complexity: O(n) where n is the length of the expression string.

The space complexity is determined by the stack stk. In the worst case, when processing the expression from right to left, we might push multiple characters onto the stack before encountering '?' operators that trigger pop operations. The maximum number of elements that can be on the stack at any time is bounded by the number of operands in the expression, which is proportional to the length of the expression string. Therefore, the space complexity is O(n).

Common Pitfalls

1. Incorrect Evaluation Order Due to Left-to-Right Processing

One of the most common mistakes is attempting to process the expression from left to right instead of right to left. Since ternary operators are right-associative, processing from left to right would require complex lookahead logic to determine where each ternary expression ends.

Incorrect Approach:

# This won't work correctly for nested expressions
stack = []
i = 0
while i < len(expression):
    if expression[i] == '?':
        # How do we know where the matching ':' is?
        # This becomes complex with nested ternaries
        ...

Solution: Always traverse from right to left as shown in the correct implementation. This naturally handles the right-associative nature of the operator.

2. Mishandling the Stack Pop Order

When evaluating a condition, the order in which values are popped from the stack matters. Since we're processing right-to-left, the false branch is pushed first, then the true branch.

Common Mistake:

if char == 'T':
    false_value = stack.pop()  # Wrong! This is actually the true value
    true_value = stack.pop()   # Wrong! This is actually the false value
    stack.append(true_value)

Correct Order:

if char == 'T':
    true_value = stack.pop()   # First pop gets the true branch
    false_value = stack.pop()  # Second pop gets the false branch
    stack.append(true_value)

3. Forgetting to Reset the Condition Flag

After processing a condition, the is_condition_next flag must be reset to False. Forgetting this causes subsequent characters to be incorrectly treated as conditions.

Bug Example:

if is_condition_next:
    if char == 'T':
        true_value = stack.pop()
        stack.pop()
        stack.append(true_value)
    # Missing: is_condition_next = False
    # This causes the next character to also be treated as a condition

4. Not Handling Both Branches of the Condition

When the condition is 'F', some might forget that we still need to pop both values but keep the false branch.

Incomplete Implementation:

if char == 'T':
    true_value = stack.pop()
    stack.pop()
    stack.append(true_value)
else:
    # Wrong: Just leaving the stack as-is
    pass  # This doesn't remove the unwanted true branch

Correct Implementation:

if char == 'T':
    true_value = stack.pop()
    stack.pop()
    stack.append(true_value)
else:
    stack.pop()  # Remove true branch
    # The false branch remains on top of the stack

5. Edge Case: Single Character Expression

While the algorithm handles this correctly, it's worth noting that expressions can be as simple as a single character like "5" or "T". Make sure your solution doesn't assume there will always be a ternary operator.

Test Cases to Verify:

  • "5" should return "5"
  • "T" should return "T"
  • "F" should return "F"

The stack-based approach naturally handles these cases since non-condition characters are simply pushed to the stack and returned.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More