Most Visited Sector in a Circular Track: Skip the Simulation
You are given n sectors labeled 1 to n arranged in a circular track. A marathon runner starts at sector rounds[0] and completes a series of rounds, where rounds[i] is the sector where round i ends. The runner always moves in increasing sector order (wrapping from n back to 1). Return the list of sectors visited the most number of times, sorted in ascending order.
This is LeetCode 1560: Most Visited Sector in a Circular Track, and it is a great example of a problem where the obvious simulation approach hides a much simpler observation.
Why this problem matters
At first glance, this looks like a simulation problem. You could trace the runner sector by sector through every round, counting visits as you go. That works, but it is slow and misses the real lesson. The problem is actually testing whether you can spot a structural invariant that makes simulation unnecessary. Problems like this train you to pause before coding and ask: "Is there a shortcut hiding in the structure?"
The key insight
Think about what happens during the middle rounds. Each full lap around the track visits every sector exactly once. If the runner completes several full laps, all sectors get the same number of visits from those laps. The visits from complete laps are perfectly balanced, so they do not affect which sectors are visited most.
The only thing that creates an imbalance is the partial segment at the beginning and end. The runner starts at rounds[0] and ends at rounds[-1]. The sectors between those two points (inclusive) get one extra visit compared to the rest. Everything else cancels out.
This means you only need two values: start = rounds[0] and end = rounds[-1].
- If
start<=end, the extra-visited sectors are simplystart, start+1, ..., end. - If
start>end, the partial segment wraps around: the extra-visited sectors are1, 2, ..., endplusstart, start+1, ..., n.
Both cases produce a sorted result directly, with no need to sort afterward.
The solution
def most_visited(n: int, rounds: list[int]) -> list[int]:
start = rounds[0]
end = rounds[-1]
if start <= end:
return list(range(start, end + 1))
return list(range(1, end + 1)) + list(range(start, n + 1))
Let's break down each piece.
start = rounds[0] is the sector where the runner begins. end = rounds[-1] is the sector where the runner finishes. These are the only two values from the input that matter.
The if start <= end branch handles the case where the partial segment does not wrap around. The sectors from start through end each get one more visit than the rest. We return them as a range.
The else branch handles the wrap-around case. The partial segment goes from start up to n, then wraps from 1 up to end. We concatenate two ranges: [1, ..., end] and [start, ..., n]. Since 1 through end comes before start through n numerically, the result is already sorted.
You never need to simulate the runner's path or count individual visits. The middle laps are full loops that add equally to every sector, so they cancel out entirely. Only the starting and ending sectors determine the answer.
Visual walkthrough
Case 1: start <= end
rounds = [1, 3, 1, 2]. Start = rounds[0] = 1, end = rounds[-1] = 2. Since 1 <= 2, the answer is sectors from start to end: [1, 2].
Middle rounds (1 -> 3 -> 1) are full loops. Every sector gets visited equally during those laps. Only sectors 1 through 2 get the extra visit.
Case 2: start > end
rounds = [3, 1, 2]. Start = rounds[0] = 3, end = rounds[-1] = 2. Since 3 > 2, the answer wraps around: sectors [1, 2] + [3, 4] = [1, 2, 3, 4].
When start > end, the partial lap wraps around the track. The result includes sectors 1 through end, plus sectors start through n.
Why middle laps cancel out
Every full lap visits every sector exactly once. If the runner completes k full laps, all sectors get +k visits. These cancel out when comparing visit counts.
Only the partial lap (from the starting sector to the ending sector) creates an imbalance in visit counts.
The formula
If start <= end: return range(start, end + 1). If start > end: return range(1, end + 1) + range(start, n + 1). Both cases produce a sorted result.
No simulation needed. The answer depends only on rounds[0], rounds[-1], and n.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Direct observation | O(n) | O(n) |
Time is O(n) because the output list can contain up to n sectors. Building the range takes time proportional to the number of sectors in the result.
Space is O(n) for the output list. No additional data structures are needed beyond the result itself.
Note that this is significantly better than a simulation approach, which would be O(total sectors traversed) and could be much larger than n if the runner completes many laps.
The building blocks
1. Circular range pattern
When you need to enumerate elements on a circular structure from position a to position b, the two-case split is the standard approach:
if a <= b:
result = list(range(a, b + 1))
else:
result = list(range(1, b + 1)) + list(range(a, n + 1))
This same pattern appears in problems involving circular arrays, ring buffers, and rotated sequences. Whenever you see a circular structure with a start and end point, think about whether the range wraps around or not.
2. Cancellation and invariance observation
Many problems that look like they require simulation have a hidden invariant: some portion of the work cancels out. In this problem, full laps cancel because they contribute equally to all sectors. The technique is to identify what part of the computation produces uniform contributions and ignore it, focusing only on the part that creates differences.
You will see this pattern in problems like Gas Station (where you skip stations that cannot be valid starts) and problems involving prefix sums where large chunks of the sum cancel.
When a problem gives you a process that repeats in cycles, always check whether the repeated portion can be ignored. If every cycle contributes the same thing to all elements, only the partial cycle at the boundaries matters.
Edge cases
- Single round:
rounds = [2]means the runner starts and ends at sector 2. The answer is[2]. Start equals end, so only one sector qualifies. - Start equals end: the runner finishes where they started. Every completed lap is a full loop. Only one sector gets the extra visit:
[start]. - Full wrap-around: if
start = 3andend = 2withn = 4, the result is[1, 2, 3, 4]. Every sector is most-visited because the partial segment covers the entire track. - n = 1: there is only one sector. The answer is always
[1]. - Many rounds, same start and end: regardless of how many intermediate rounds exist, only
rounds[0]androunds[-1]determine the answer. - Consecutive sectors:
start = 3,end = 3returns[3]. The runner ends exactly where they started, so only that one sector has the extra visit.
From understanding to recall
You have seen why simulation is unnecessary and how the two-case range formula works. The logic is clean. But can you reproduce the wrap-around case correctly under pressure? It is easy to get the boundary wrong, to forget whether the range should include start and end, or to reverse the order of the two concatenated ranges.
Spaced repetition closes that gap. You practice writing the circular range pattern from scratch at increasing intervals. After a few rounds, the two-case split is automatic. You see "circular track, find the most visited" and the formula comes out instantly without second-guessing.
The circular range pattern and cancellation observation are two of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Learning them individually and drilling them with spaced repetition is far more effective than grinding random problems and hoping patterns stick.
Related posts
- Rotate Array - Another problem involving circular indexing in arrays
- Gas Station - Circular traversal with greedy observation to avoid full simulation
- Circular Sentence - Pattern matching on circular structures
CodeBricks breaks the Most Visited Sector problem into its circular range and cancellation observation building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When an observation-based problem shows up in your interview, you do not think about it. You just write it.