The Overwritten Hour
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- mid
- Asked in
- Qube Research & Technologies
Problem
A fixed-size ring buffer stores tick timestamps in ascending order, but once the writer looped back to the start it overwrote the head, so the values now read as one ascending run that wraps around a single pivot. Given readings in that wrapped order and a target, return the index where target sits, or -1 if it is not present. The values are distinct, and the buffer is large enough that a probe should lean on its ordering rather than touch every slot.
The log wrapped on itself. The order is still there, just not where you would look.
Example 1
Input:
target = 12, readings = [40,55,70,5,12,28]
Output:
4
Example 2
Input:
target = 99, readings = [40,55,70,5,12,28]
Output:
-1
Worked solution and explanation
Why this problem exists in real interviews
This is binary search wearing a costume. The ring buffer wrapped, so readings is sorted but not from index 0: there is one pivot where a high value is followed by the lowest. The skill being probed is whether you can still halve the search space when the global sort is broken. Anyone can write a textbook binary search. The trick is that comparing target to readings[mid] alone is not enough, because either side of mid might straddle the pivot. Treat it as an ordinary sorted array and you follow the wrong half past the wrap and report -1 for values that are sitting right there. Fall back to scanning every slot and you are correct but you have thrown away the ordering the whole structure exists to give you.
Break down the requirements
Step 1: Identify the sorted half
With lo, mid, hi in hand, check readings[lo] <= readings[mid]. If true, the left half from lo to mid is sorted and the pivot (if any) is on the right. If false, the pivot is on the left and the right half from mid to hi is the sorted one.
Step 2: Ask if the target is inside the clean half
The sorted half has an unambiguous range. For a sorted left half, test readings[lo] <= target < readings[mid]. If the target falls in that window, discard the right half (hi = mid - 1); otherwise the target must be in the messy half, so lo = mid + 1. Mirror the logic when the right half is the sorted one.
Step 3: Mind the boundary comparisons
Use <= on the endpoint that includes readings[lo] (or readings[hi]) and strict < on the side touching readings[mid], which you already handled with the equality check at the top. Getting one of these wrong drops the element sitting exactly on a boundary and turns a present value into a -1.
Step 4: Terminate cleanly
Loop while lo <= hi. Return mid the moment readings[mid] equals target. If the window collapses without a hit, the value is not in the buffer, so return -1. This also handles the empty buffer for free: hi starts at -1 and the loop never runs.
The solution
def find_reading(readings, target):
lo, hi = 0, len(readings) - 1
while lo <= hi:
mid = (lo + hi) // 2
if readings[mid] == target:
return mid
if readings[lo] <= readings[mid]:
# left half [lo, mid] is sorted
if readings[lo] <= target < readings[mid]:
hi = mid - 1
else:
lo = mid + 1
else:
# right half [mid, hi] is sorted
if readings[mid] < target <= readings[hi]:
lo = mid + 1
else:
hi = mid - 1
return -1- Plain binary search
- Compares target to readings[mid] and moves left or right on that alone. Correct only when the array is sorted from index 0. Cross the pivot and it steers into the wrong half, missing present values.
- Rotation-aware
- First asks which half is sorted, then uses that half's clean bounds to decide direction. Correct for any rotation amount, including a rotation of zero (already sorted) and a single element.
Common follow-up questions
- What changes if the timestamps can repeat, so readings may contain duplicates? (Duplicates break the `readings[lo] <= readings[mid]` test (both sides can be equal), forcing a lo += 1 fallback and pushing the worst case to O(n).)
- How would you find the pivot index (the smallest element) instead of a specific target? (Tests whether the candidate can adapt the same invariant to search for a structural boundary rather than a value.)
- Several threads probe this buffer concurrently while one writer appends. Does the GIL make this safe, and what would you still guard? (Ties to CPython's GIL: individual list index reads are atomic, but a read racing a rotation or rewrite still needs a snapshot or lock for a consistent view.)