C++ 1114.cpp
class Foo {
volatile int state;
public:
Foo() {
state = 1;
}
void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
state++;
}
void second(function<void()> printSecond) {
while (state != 2);
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
state++;
}
void third(function<void()> printThird) {
while (state != 3);
// printThird() outputs "third". Do not change or remove this line.
printThird();
}
};
Difficulty
and has an acceptance rate of about 73% (300k+ submissions).
Typical patterns to solve this problem:
- Concurrency
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The class Foo coordinates three actions-first, second, and third-so that they are executed in a strict order regardless of the order in which the corresponding member functions are invoked from different threads. A single integer field, declared as volatile, serves as the shared state that all threads observe. The constructor initializes this state, and each method updates or waits on the state to enforce the required sequencing.
Constructor
The constructor sets the initial state to 1, indicating that the first action is ready to run.
Foo() {
state = 1;
}
Because state is volatile, the write is immediately visible to any thread that subsequently reads the variable.
first Method
first receives a callable that prints the word “first”. The method invokes the callable, then increments the shared state to signal that the first step has completed.
void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
state++;
}
The increment changes the state from 1 to 2, allowing the second method to proceed.
second Method
second begins by busy-waiting until the shared state equals 2. This loop blocks the thread until the first method has performed its increment. Once the condition is satisfied, the method calls the supplied printable function and then increments the state to 3.
void second(function<void()> printSecond) {
while (state != 2);
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
state++;
}
The waiting loop relies on the volatile qualifier to guarantee that each read of state reflects the most recent write from another thread.
third Method
third also uses a busy-wait, this time for the state to become 3. When the condition is met, it invokes the provided printable function. No further state change is required because this is the final step.
void third(function<void()> printThird) {
while (state != 3);
// printThird() outputs "third". Do not change or remove this line.
printThird();
}
Edge Cases
The implementation gracefully handles the situation where the methods are called out of order. If second or third are invoked before the preceding step has completed, each method’s while-loop will spin until the expected state value appears, effectively serializing the execution without deadlocking. The use of a volatile integer ensures that even if a thread experiences a context switch or runs on a different core, it will observe the most recent state value.
Another implicit edge case is the scenario where the same method is called multiple times from different threads. Because each method increments the state only after its own printable action, subsequent calls will find the state already advanced and will either proceed immediately (if the state matches) or continue spinning until the state reaches the required value. This behavior prevents premature execution of later steps.
Performance Considerations
The primary performance characteristic stems from the busy-waiting loops in second and third. By continuously reading the volatile variable, the code avoids the overhead of kernel-level synchronization primitives such as mutexes or condition variables. For short waiting periods, this can be more efficient because it eliminates context switches and kernel scheduling latency.
Incrementing an int is an atomic operation on most modern architectures when the variable is aligned, and the volatile qualifier prevents the compiler from caching the value in a register. This ensures that each read reflects the latest write, allowing the spin loops to detect state changes promptly.
Because the state transitions are monotonic (1 → 2 → 3) and each transition occurs exactly once, the loops terminate quickly after the preceding step finishes, limiting the amount of wasted CPU cycles. The overall design therefore achieves ordering with minimal synchronization scaffolding while keeping the runtime overhead low for the intended use case.
ELI5 - Simplified Explanation
Think of this code like three friends who want to take turns playing a game in a specific order. The friend named first goes right away because they are ready. They have a little counter called state that starts at one. After the first friend finishes, they change the counter to two so the next person knows it is time. The second friend is waiting patiently and keeps checking the counter over and over again. As soon as the counter becomes two, the second friend knows it is their turn to play, and when they finish, they change the counter to three. The third friend is also waiting and watching the counter. They do not do anything until the counter reaches three, which shows that the first two friends are done. Once the counter hits three, the third friend finally gets to play their part. This makes sure that even though they are all different parts of the program, they follow the right order every single time.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | |||||||
|---|---|---|---|---|---|---|---|
| author | solutions | ||||||
| www | AlgoMonster | C++ | Java | Python | TS | ||
| WalkCCC | C++ | Java | Python | ||||
| leetcode.ca | C++ | Java | Python | ||||
| Progiez | C++ | Java | Python | ||||
| github | BigEggStudy | C# | |||||
| black-shadows | C++ | ||||||
| chandansgowda | Python | ||||||
| charles-wangkai | Java | ||||||
| DimitrisJim | C | ||||||
| everthis | C++ | ||||||
| fsq | C++ | ||||||
| keineahnung2345 | C++ | ||||||
| Krazune | Java | ||||||
| lkwq007 | Python | ||||||
| suxrobGM | C# | ||||||
| varunu28 | Java | ||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||
| WalkCCC | C++, Python, Java | ||||||
| leetcode.ca | C++, Java, Python, Python | ||||||
| Progiez | C++, Java, Python | ||||||
| BigEggStudy | C# | ||||||
| black-shadows | C++, C++ | ||||||
| chandansgowda | Python | ||||||
| charles-wangkai | Java | ||||||
| DimitrisJim | C | ||||||
| everthis | C++ | ||||||
| fsq | C++ | ||||||
| keineahnung2345 | C++ | ||||||
| Krazune | Java | ||||||
| lkwq007 | Python | ||||||
| suxrobGM | C# | ||||||
| varunu28 | Java |
Videos
Similar Problems
- problem 1115: Print FooBar Alternately
Solved On The Same Day
- problem 1: Two Sum
- problem 9: Palindrome Number
- problem 14: Longest Common Prefix
- problem 20: Valid Parentheses
- problem 26: Remove Duplicates from Sorted Array
- problem 27: Remove Element
- problem 83: Remove Duplicates from Sorted List
- problem 88: Merge Sorted Array
- problem 94: Binary Tree Inorder Traversal
- problem 100: Same Tree
- problem 104: Maximum Depth of Binary Tree
- problem 121: Best Time to Buy and Sell Stock
- problem 136: Single Number
- problem 141: Linked List Cycle
- problem 169: Majority Element
- problem 190: Reverse Bits
- problem 226: Invert Binary Tree
- problem 234: Palindrome Linked List
- problem 3: Longest Substring Without Repeating Characters
- problem 7: Reverse Integer
- problem 8: String to Integer (atoi)
- problem 11: Container With Most Water
- problem 12: Integer to Roman
- problem 15: 3Sum
- problem 17: Letter Combinations of a Phone Number
- problem 22: Generate Parentheses
- problem 31: Next Permutation
- problem 36: Valid Sudoku
- problem 39: Combination Sum
- problem 40: Combination Sum II
- problem 45: Jump Game II
- problem 46: Permutations
- problem 47: Permutations II
- problem 48: Rotate Image
- problem 49: Group Anagrams
- problem 55: Jump Game
- problem 59: Spiral Matrix II
- problem 75: Sort Colors
- problem 77: Combinations
- problem 91: Decode Ways
- problem 95: Unique Binary Search Trees II
- problem 96: Unique Binary Search Trees
- problem 134: Gas Station
- problem 164: Maximum Gap
- problem 189: Rotate Array
- problem 215: Kth Largest Element in an Array
- problem 238: Product of Array Except Self
- problem 287: Find the Duplicate Number
- problem 322: Coin Change
- problem 347: Top K Frequent Elements
- problem 377: Combination Sum IV
- problem 438: Find All Anagrams in a String
- problem 474: Ones and Zeroes
- problem 518: Coin Change II
- problem 526: Beautiful Arrangement
- problem 763: Partition Labels
- problem 983: Minimum Cost For Tickets
- problem 994: Rotting Oranges
- problem 1027: Longest Arithmetic Subsequence
- problem 2140: Solving Questions With Brainpower
- problem 2466: Count Ways To Build Good Strings
- problem 4: Median of Two Sorted Arrays
- problem 41: First Missing Positive
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality