Facebook Pixel

682. Baseball Game

Problem Description

You need to calculate the total score of a baseball game that follows special scoring rules. The game starts with an empty score record.

You're given a list of strings called operations, where each operation tells you how to update the score record. There are four types of operations:

  1. Integer x: Add a new score of value x to the record
  2. '+': Add a new score that equals the sum of the two most recent scores
  3. 'D': Add a new score that is double the most recent score
  4. 'C': Cancel and remove the most recent score from the record

Your task is to process all operations in order and return the sum of all scores remaining in the record after all operations are applied.

For example, if you have operations ["5", "2", "C", "D", "+"]:

  • "5": Record score 5. Record: [5]
  • "2": Record score 2. Record: [5, 2]
  • "C": Cancel last score. Record: [5]
  • "D": Double the last score (5 × 2 = 10). Record: [5, 10]
  • "+": Sum of last two scores (5 + 10 = 15). Record: [5, 10, 15]
  • Final sum: 5 + 10 + 15 = 30

The problem guarantees that all operations will be valid (you won't get a "+" when there are fewer than two scores, etc.) and all numbers fit within a 32-bit integer range.

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

Intuition

The key insight is recognizing that we need to maintain a dynamic record of scores where we can:

  • Add new scores to the end
  • Access the most recent scores (last one or last two)
  • Remove the most recent score

These operations naturally point us toward using a stack data structure, since stacks excel at operations involving the "most recent" or "last" elements through their Last-In-First-Out (LIFO) property.

When we think about each operation:

  • Adding a number: We simply push it onto the stack
  • "+" operation: We need the last two scores, which are at the top of the stack (accessible via stack[-1] and stack[-2])
  • "D" operation: We need only the last score to double it (accessible via stack[-1])
  • "C" operation: We need to remove the last score, which is exactly what pop() does

The stack perfectly mirrors our score record - the top of the stack is always the most recent score, and we can easily access previous scores by their position from the top. After processing all operations, the stack contains exactly the valid scores we need to sum up.

This approach is intuitive because we're essentially simulating the score-keeping process directly - maintaining a list of scores and modifying it according to the rules, with the stack providing efficient access to the most recent entries that the operations care about.

Learn more about Stack patterns.

Solution Approach

We implement the solution using a stack to simulate the scoring process step by step.

Algorithm Steps:

  1. Initialize an empty stack stk to store the scores.

  2. Iterate through each operation in the operations list:

    • If operation is "+": Calculate the sum of the top two elements (stk[-1] + stk[-2]) and push the result onto the stack
    • If operation is "D": Double the top element (stk[-1] * 2 or stk[-1] << 1 using bit shift) and push the result onto the stack
    • If operation is "C": Remove the top element using stk.pop()
    • Otherwise (it's a number string): Convert it to an integer using int(op) and push onto the stack
  3. After processing all operations, return the sum of all elements in the stack using sum(stk).

Implementation Details:

The solution uses a bit shift operation stk[-1] << 1 to double the value, which is equivalent to multiplying by 2 but can be slightly more efficient. The left shift by 1 position in binary effectively doubles the number.

For accessing previous scores:

  • stk[-1] gives us the most recent score (top of stack)
  • stk[-2] gives us the second most recent score

The beauty of this approach is its simplicity - we directly simulate what's happening in the game, and the stack naturally maintains the order and allows easy access to recent scores. Each operation takes O(1) time, making the overall time complexity O(n) where n is the number of operations.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's trace through a simple example with operations ["5", "-2", "4", "C", "D", "9", "+", "C"]:

Initial state: Stack is empty []

Step 1: Operation = "5" (integer)

  • This is a number, so convert to integer and push onto stack
  • Stack: [5]

Step 2: Operation = "-2" (integer)

  • This is a number, so convert to integer and push onto stack
  • Stack: [5, -2]

Step 3: Operation = "4" (integer)

  • This is a number, so convert to integer and push onto stack
  • Stack: [5, -2, 4]

Step 4: Operation = "C" (cancel)

  • Remove the most recent score using pop()
  • Stack: [5, -2]

Step 5: Operation = "D" (double)

  • Double the most recent score: stk[-1] * 2 = -2 * 2 = -4
  • Push -4 onto stack
  • Stack: [5, -2, -4]

Step 6: Operation = "9" (integer)

  • This is a number, so convert to integer and push onto stack
  • Stack: [5, -2, -4, 9]

Step 7: Operation = "+" (sum last two)

  • Sum the two most recent scores: stk[-1] + stk[-2] = 9 + (-4) = 5
  • Push 5 onto stack
  • Stack: [5, -2, -4, 9, 5]

Step 8: Operation = "C" (cancel)

  • Remove the most recent score using pop()
  • Stack: [5, -2, -4, 9]

Final step: Calculate the sum of all remaining scores

  • Sum = 5 + (-2) + (-4) + 9 = 8

The function returns 8.

This walkthrough demonstrates how the stack maintains our score record throughout the game, with the top of the stack always representing the most recent score, making operations like cancel, double, and sum efficient and straightforward.

Solution Implementation

1class Solution:
2    def calPoints(self, operations: List[str]) -> int:
3        """
4        Calculate the sum of scores after applying all operations in a baseball game.
5      
6        Args:
7            operations: List of strings representing operations to perform
8      
9        Returns:
10            The sum of all valid scores after applying all operations
11        """
12        # Initialize stack to keep track of valid scores
13        score_stack = []
14      
15        # Process each operation
16        for operation in operations:
17            if operation == "+":
18                # Add sum of last two scores
19                score_stack.append(score_stack[-1] + score_stack[-2])
20            elif operation == "D":
21                # Double the last score (using bit shift left by 1)
22                score_stack.append(score_stack[-1] << 1)
23            elif operation == "C":
24                # Cancel/remove the last score
25                score_stack.pop()
26            else:
27                # It's a number, convert string to integer and add to stack
28                score_stack.append(int(operation))
29      
30        # Return the sum of all remaining valid scores
31        return sum(score_stack)
32
1class Solution {
2    public int calPoints(String[] operations) {
3        // Use a stack to track scores
4        Deque<Integer> scoreStack = new ArrayDeque<>();
5      
6        // Process each operation
7        for (String operation : operations) {
8            if ("+".equals(operation)) {
9                // Add sum of last two scores
10                int lastScore = scoreStack.pop();
11                int secondLastScore = scoreStack.peek();
12                scoreStack.push(lastScore);  // Restore the last score
13                scoreStack.push(lastScore + secondLastScore);  // Push the sum
14            } else if ("D".equals(operation)) {
15                // Double the last score
16                scoreStack.push(scoreStack.peek() * 2);  // Using multiplication instead of bit shift for clarity
17            } else if ("C".equals(operation)) {
18                // Cancel the last score
19                scoreStack.pop();
20            } else {
21                // Record a new score (numeric value)
22                scoreStack.push(Integer.parseInt(operation));
23            }
24        }
25      
26        // Calculate and return the sum of all scores
27        return scoreStack.stream()
28                .mapToInt(Integer::intValue)
29                .sum();
30    }
31}
32
1class Solution {
2public:
3    int calPoints(vector<string>& operations) {
4        // Use a stack to keep track of valid scores
5        vector<int> scoreStack;
6      
7        // Process each operation in the operations array
8        for (const auto& operation : operations) {
9            int stackSize = scoreStack.size();
10          
11            if (operation == "+") {
12                // Add the sum of the last two scores to the stack
13                int sumOfLastTwo = scoreStack[stackSize - 1] + scoreStack[stackSize - 2];
14                scoreStack.push_back(sumOfLastTwo);
15            } 
16            else if (operation == "D") {
17                // Double the last score and add it to the stack
18                int doubledScore = scoreStack[stackSize - 1] * 2;  // Using * 2 instead of << 1 for clarity
19                scoreStack.push_back(doubledScore);
20            } 
21            else if (operation == "C") {
22                // Cancel/remove the last score from the stack
23                scoreStack.pop_back();
24            } 
25            else {
26                // The operation is a number, convert it to integer and add to stack
27                int score = stoi(operation);
28                scoreStack.push_back(score);
29            }
30        }
31      
32        // Calculate and return the sum of all valid scores
33        return accumulate(scoreStack.begin(), scoreStack.end(), 0);
34    }
35};
36
1/**
2 * Calculate the sum of points in a baseball game based on operations
3 * @param operations - Array of strings representing operations
4 * @returns The sum of all valid points
5 */
6function calPoints(operations: string[]): number {
7    // Stack to store valid scores
8    const scoreStack: number[] = [];
9  
10    // Process each operation
11    for (const operation of operations) {
12        if (operation === '+') {
13            // Add sum of last two scores
14            const lastScore = scoreStack.at(-1)!;
15            const secondLastScore = scoreStack.at(-2)!;
16            scoreStack.push(lastScore + secondLastScore);
17        } else if (operation === 'D') {
18            // Double the last score (using left shift for multiplication by 2)
19            const lastScore = scoreStack.at(-1)!;
20            scoreStack.push(lastScore << 1);
21        } else if (operation === 'C') {
22            // Cancel/remove the last score
23            scoreStack.pop();
24        } else {
25            // Parse and add numeric score
26            scoreStack.push(Number(operation));
27        }
28    }
29  
30    // Calculate and return the sum of all scores
31    return scoreStack.reduce((sum, score) => sum + score, 0);
32}
33

Time and Space Complexity

Time Complexity: O(n), where n is the length of the operations list.

The algorithm iterates through the operations list exactly once. For each operation:

  • "+" operation: Accesses the last two elements and performs addition - O(1)
  • "D" operation: Accesses the last element and performs left shift (multiplication by 2) - O(1)
  • "C" operation: Removes the last element from the stack - O(1)
  • Integer string: Converts string to integer and appends to stack - O(1)

After the loop, sum(stk) iterates through all elements in the stack, which in the worst case contains n elements, taking O(n) time.

Therefore, the overall time complexity is O(n) + O(n) = O(n).

Space Complexity: O(n), where n is the length of the operations list.

The algorithm uses a stack stk to store integer values. In the worst case, when all operations are integer values (no "C" operations to remove elements), the stack will contain n elements. Even with "+" and "D" operations that add elements based on existing ones, and "C" operations that remove elements, the maximum size of the stack is bounded by n.

Therefore, the space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect String to Integer Conversion

A common mistake is trying to check if an operation is a number using methods like operation.isdigit() or operation.isnumeric(). These methods fail for negative numbers because they don't recognize the minus sign as part of a digit.

Incorrect approach:

if operation.isdigit():  # This fails for negative numbers like "-4"
    score_stack.append(int(operation))

Correct approach: Use try-except or check against known operations:

# Option 1: Check against known operations (recommended)
if operation not in ["+", "D", "C"]:
    score_stack.append(int(operation))

# Option 2: Try-except block
try:
    score_stack.append(int(operation))
except ValueError:
    # Handle special operations
    if operation == "+":
        # ...

2. Not Handling Edge Cases in Early Operations

While the problem guarantees valid operations, developers often write defensive code that checks for edge cases unnecessarily, making the code more complex:

Overly defensive (unnecessary):

if operation == "+":
    if len(score_stack) >= 2:  # Unnecessary check
        score_stack.append(score_stack[-1] + score_stack[-2])

Clean approach (trusting the problem constraints):

if operation == "+":
    score_stack.append(score_stack[-1] + score_stack[-2])

3. Using Wrong Data Structure

Some might use a list and try to track the "current position" with an index instead of using it as a stack, leading to complex index management:

Problematic approach:

scores = []
index = -1
for operation in operations:
    if operation == "C":
        index -= 1  # Wrong! This doesn't remove the element
    # ... complex index tracking

Correct approach: Simply use list methods as stack operations (append/pop).

4. Misunderstanding the "+" Operation

A common misinterpretation is thinking "+" means to add the last score to itself, rather than summing the last two scores:

Wrong interpretation:

if operation == "+":
    score_stack.append(score_stack[-1] * 2)  # Wrong!

Correct interpretation:

if operation == "+":
    score_stack.append(score_stack[-1] + score_stack[-2])
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

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

Load More