Facebook Pixel

3894. Traffic Signal Color

Easy
LeetCode ↗

Problem Description

You are given an integer timer that represents the remaining time (in seconds) on a traffic signal. Your task is to determine the current state of the signal based on the value of timer.

The signal follows these rules:

  • If timer == 0, the signal is "Green".
  • If timer == 30, the signal is "Orange".
  • If 30 < timer <= 90, the signal is "Red".

You need to return the current state of the signal as a string. If the value of timer does not satisfy any of the above conditions, return "Invalid".

For example, if timer is 0, you return "Green"; if timer is 45, you return "Red"; and if timer is 100, you return "Invalid" since it falls outside all the defined ranges.

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

How We Pick the Algorithm

Why Simulation / Basic DSA?

This problem maps to Simulation / Basic DSA through a short path in the full flowchart.

DirecttransformationoryesComplexdatastructure?noSimulation /Basic DSA

Following the described procedure step by step produces the solution.

Open in Flowchart

Intuition

The problem gives us a set of clear, well-defined conditions, and each condition maps directly to a specific output string. This is a signal that we can solve the problem by simply checking each condition in order and returning the matching result.

The key observation is that the conditions are mutually exclusive: a given value of timer can satisfy at most one of the rules. This means we can evaluate them one by one. As soon as a condition is met, we return its corresponding string immediately, and there is no need to check the remaining conditions.

We start by checking the exact-match cases first: timer == 0 returns "Green" and timer == 30 returns "Orange". Then we check the range case 30 < timer <= 90, which returns "Red". Since the value 30 is already handled by the earlier exact-match check, we do not have to worry about overlap with the range condition.

Finally, if none of these conditions hold, the value of timer is outside all the valid cases, so we return "Invalid" as a fallback. This straightforward "check and return" strategy naturally leads us to a direct simulation of the rules described in the problem.

Solution Approach

Solution 1: Simulation

We determine the answer according to the conditions described in the problem and return the corresponding string.

We translate each rule into a conditional check, evaluating them in order:

  1. First, check if timer == 0. If so, return "Green".
  2. Next, check if timer == 30. If so, return "Orange".
  3. Then, check if 30 < timer <= 90. If so, return "Red".
  4. If none of the above conditions are satisfied, return "Invalid".

By placing the exact-match checks (timer == 0 and timer == 30) before the range check, we ensure there is no ambiguity when the value falls on a boundary. The first matching condition triggers an immediate return, so the order of checks guarantees correct behavior.

The time complexity is O(1) and the space complexity is O(1), since we only perform a fixed number of comparisons and use no extra space.

class Solution:
    def trafficSignal(self, timer: int) -> str:
        if timer == 0:
            return "Green"
        if timer == 30:
            return "Orange"
        if 30 < timer <= 90:
            return "Red"
        return "Invalid"

Example Walkthrough

Let's trace through the solution using a small example with timer = 45.

We evaluate each conditional check in the order written in the code:

  1. Check timer == 0? Is 45 == 0? No. We skip the "Green" return and move on.

  2. Check timer == 30? Is 45 == 30? No. We skip the "Orange" return and move on.

  3. Check 30 < timer <= 90? Is 30 < 45 <= 90? We break this into two parts:

    • Is 30 < 45? Yes.
    • Is 45 <= 90? Yes.

    Both parts are true, so the whole condition holds. We immediately return "Red".

Because the third condition matched, we return "Red" right away and never reach the final "Invalid" fallback.

To see how the fallback works, let's quickly trace timer = 100:

  1. Check timer == 0? Is 100 == 0? No.
  2. Check timer == 30? Is 100 == 30? No.
  3. Check 30 < timer <= 90? Is 100 <= 90? No, so the range condition fails.
  4. None of the conditions matched, so we fall through to the final line and return "Invalid".

This demonstrates the "check and return" strategy: we test each rule in sequence, return the moment one matches, and rely on the "Invalid" fallback when none of them apply.

Solution Implementation

1class Solution:
2    def trafficSignal(self, timer: int) -> str:
3        # Timer at 0 seconds indicates the Green signal
4        if timer == 0:
5            return "Green"
6
7        # Timer at exactly 30 seconds indicates the Orange signal
8        if timer == 30:
9            return "Orange"
10
11        # Timer in the range (30, 90] indicates the Red signal
12        if 30 < timer <= 90:
13            return "Red"
14
15        # Any other timer value is considered invalid
16        return "Invalid"
17
1class Solution {
2
3    // Timer value (in seconds) at which the signal turns green
4    private static final int GREEN_TIME = 0;
5
6    // Timer value at which the signal turns orange
7    private static final int ORANGE_TIME = 30;
8
9    // Upper bound (inclusive) of the timer range for the red signal
10    private static final int RED_TIME_LIMIT = 90;
11
12    /**
13     * Returns the traffic signal color corresponding to the given timer value.
14     *
15     * @param timer the elapsed time in seconds
16     * @return "Green", "Orange", "Red", or "Invalid" based on the timer value
17     */
18    public String trafficSignal(int timer) {
19        // Signal is green only at the exact start time
20        if (timer == GREEN_TIME) {
21            return "Green";
22        }
23
24        // Signal turns orange at the exact transition time
25        if (timer == ORANGE_TIME) {
26            return "Orange";
27        }
28
29        // Signal stays red for the range after orange, up to the limit
30        if (timer > ORANGE_TIME && timer <= RED_TIME_LIMIT) {
31            return "Red";
32        }
33
34        // Any other timer value is considered invalid
35        return "Invalid";
36    }
37}
38
1class Solution {
2public:
3    // Returns the traffic signal state based on the given timer value.
4    // - timer == 0            -> "Green"
5    // - timer == 30           -> "Orange"
6    // - 30 < timer <= 90      -> "Red"
7    // - otherwise             -> "Invalid"
8    string trafficSignal(int timer) {
9        // Signal is Green at the start of the cycle.
10        if (timer == 0) {
11            return "Green";
12        }
13
14        // Signal turns Orange exactly at the 30-second mark.
15        if (timer == 30) {
16            return "Orange";
17        }
18
19        // Signal is Red for the range (30, 90].
20        if (timer > 30 && timer <= 90) {
21            return "Red";
22        }
23
24        // Any timer value outside the defined ranges is invalid.
25        return "Invalid";
26    }
27};
28
1/**
2 * Determines the traffic signal color based on the given timer value.
3 *
4 * Mapping rules (preserved from original logic):
5 *   - timer === 0            -> 'Green'
6 *   - timer === 30           -> 'Orange'
7 *   - timer in (30, 90]      -> 'Red'
8 *   - any other value        -> 'Invalid'
9 *
10 * @param timer - The current timer value in seconds.
11 * @returns The corresponding signal color, or 'Invalid' for out-of-range values.
12 */
13function trafficSignal(timer: number): string {
14    // Green light: the cycle starts at zero.
15    if (timer === 0) {
16        return 'Green';
17    }
18
19    // Orange light: exact transition point at 30.
20    if (timer === 30) {
21        return 'Orange';
22    }
23
24    // Red light: any value greater than 30 and up to (and including) 90.
25    if (timer > 30 && timer <= 90) {
26        return 'Red';
27    }
28
29    // Fallback: any value not matching the rules above is considered invalid.
30    return 'Invalid';
31}
32```
33
34A few perspectives on improving this further, depending on your needs:
35
361. **Type safety with a union return type** — Restrict the return to known values so callers get compile-time checking:
37   ```typescript
38   type SignalColor = 'Green' | 'Orange' | 'Red' | 'Invalid';
39   function trafficSignal(timer: number): SignalColor { /* ... */ }
40

Time and Space Complexity

The time complexity is O(1), and the space complexity is O(1).

The function trafficSignal performs a fixed number of comparisons against constant values (timer == 0, timer == 30, 30 < timer <= 90) and returns a string immediately. Since the number of operations does not depend on the input value timer, the execution time is constant, giving a time complexity of O(1).

No additional data structures are allocated, and only a constant amount of memory is used regardless of the input. Therefore, the space complexity is O(1).

Common Pitfalls

Pitfall 1: Getting the boundary conditions wrong on the "Red" range

The most common mistake is mishandling the boundaries of the range check 30 < timer <= 90. Developers frequently write one of the following incorrect variants:

  • 30 <= timer <= 90 — This wrongly includes 30, but since the timer == 30 check comes first and returns early, the bug is masked here. However, if you ever reorder the checks, timer == 30 would incorrectly fall into the "Red" branch.
  • 30 < timer < 90 — This wrongly excludes 90, causing trafficSignal(90) to return "Invalid" instead of "Red".
  • 30 < timer <= 91 — An off-by-one error that wrongly includes 91.

Solution: Carefully translate the inequality exactly as stated in the problem (30 < timer <= 90). Test the exact boundary values — 30, 31, 90, and 91 — to confirm correct behavior:

sol = Solution()
assert sol.trafficSignal(30) == "Orange"   # exact match, not Red
assert sol.trafficSignal(31) == "Red"      # lower bound (exclusive) + 1
assert sol.trafficSignal(90) == "Red"      # upper bound (inclusive)
assert sol.trafficSignal(91) == "Invalid"  # just past upper bound

Pitfall 2: Ordering the conditions incorrectly

If the range check is placed before the exact-match checks, a boundary value can be captured by the wrong branch. For example, if you wrote:

if 30 <= timer <= 90:   # using <= by mistake AND checking first
    return "Red"
if timer == 30:
    return "Orange"     # unreachable for timer == 30!

Here timer == 30 would always return "Red", and the "Orange" branch becomes dead code.

Solution: Always place the exact-match checks (timer == 0, timer == 30) before the range check. The first matching condition returns immediately, so a specific value is resolved before it ever reaches a broader range.

Pitfall 3: Forgetting negative or out-of-range values

Since the problem mentions timer represents remaining time, it's easy to assume the input is always between 0 and 90. But values like -5, 15, or 200 are valid inputs that must return "Invalid".

Solution: Rely on the final fallback return "Invalid" to catch everything not explicitly matched. Avoid adding restrictive assumptions about the input range that the problem doesn't guarantee:

assert sol.trafficSignal(-5) == "Invalid"
assert sol.trafficSignal(15) == "Invalid"
assert sol.trafficSignal(200) == "Invalid"

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.


Recommended Readings

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

Load More