The Half-Life Search
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
- L4
Problem
A lookup service keeps its record IDs as a sorted list of integers and resolves a query to the slot it occupies. Return the index of target in nums, or -1 when it is absent. The list can be large and the ordering is guaranteed, so the lookup should not have to scan every element.
Every guess cuts the problem in half.
Example 1
Input:
nums = [1,3,5,7,9,11,13], target = 7
Output:
3
Example 2
Input:
nums = [1,3,5,7,9], target = 6
Output:
-1
Worked solution and explanation
What this problem really is
Underneath the lookup-service framing, this is two pointers closing on a target through systematic halving, and the whole problem lives in the boundary conditions. Anyone can sketch the midpoint compare. What separates candidates is the loop termination and the pointer updates: get either one slightly wrong and the function either hangs forever on a two-element range or skips the one slot that holds the answer. The sorted guarantee is the permission slip to throw away half the array every iteration; if you scan linearly you have answered a different, easier question.
Walk through the decisions
Step 1: Bracket the whole array, inclusively
Set low = 0 and high = len(nums) - 1. These bracket the inclusive range still in play. Starting high at len(nums) instead is the off-by-one that reads past the end on the first compare.
Step 2: Halve, and always shrink the range
Compute mid = (low + high) // 2, compare nums[mid] to the target, and move the pointer on the losing side past mid: low = mid + 1 or high = mid - 1. The + 1 / - 1 is what guarantees the range actually shrinks. Assigning low = mid lets a two-element window repeat forever.
Step 3: Terminate on a crossed bracket
Loop while low <= help is wrong on purpose to make you read it: the condition is low <= high. The <= keeps the single-element range alive long enough to check it. Return mid on a hit; fall out of the loop to return -1 when the brackets cross.
The solution
def binary_search(nums, target):
low = 0
high = len(nums) - 1
while low <= high:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1Common follow-up questions
- If the list has duplicates, how would you return the leftmost occurrence of the target? (Tests continuing the search after a match by setting `high = mid - 1` and remembering the candidate index, rather than returning early.)
- In a language with 32-bit ints, what is wrong with `(low + high) // 2`, and how do you fix it? (Tests awareness that `low + (high - low) // 2` avoids overflow in fixed-width-integer languages, and that Python's arbitrary precision makes it a non-issue here.)
- How would you adapt this to search a sorted array that has been rotated at an unknown pivot? (Tests a modified binary search that first locates the pivot, or compares against the sorted half each iteration.)