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:
- Integer
x
: Add a new score of valuex
to the record '+'
: Add a new score that equals the sum of the two most recent scores'D'
: Add a new score that is double the most recent score'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.
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 viastack[-1]
andstack[-2]
)"D"
operation: We need only the last score to double it (accessible viastack[-1]
)"C"
operation: We need to remove the last score, which is exactly whatpop()
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:
-
Initialize an empty stack
stk
to store the scores. -
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
orstk[-1] << 1
using bit shift) and push the result onto the stack - If operation is
"C"
: Remove the top element usingstk.pop()
- Otherwise (it's a number string): Convert it to an integer using
int(op)
and push onto the stack
- If operation is
-
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 EvaluatorExample 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])
How many times is a tree node visited in a depth first search?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!