Look in the middle first
Think of the guessing game where someone picks a number from 1 to 100 and only says "higher" or "lower". You would start at 50. Because the list is sorted, the middle number tells you which half the value must be in.
If the middle is too small, the value can only be to its right. If it is too big, only to its left. One look, and half the list is gone.
The middle is position 4, value 23. 31 is bigger, so 23 and everything left of it are out.
Keep a range, not a position
Two markers, low and high, hold the part of the list that can still contain the value. Start with the whole list. Each look shrinks the range, like closing in on a word by opening a dictionary to the middle again and again.
If low passes high, the range is empty and the value isn't there.
answers = []
for x in queries:
low, high, at = 0, len(nums) - 1, -1
while low <= high:
mid = (low + high) // 2
if nums[mid] == x:
at = mid
break
if nums[mid] < x:
low = mid + 1
else:
high = mid - 1
answers.append(at)
return answersWhy not just use a set?
A set from Hashing can tell you "yes, it's there". But a sorted list is often what you are given, and halving needs no extra memory. It also tells you where the value sits.
That opens up the next steps: where a missing value would go, and how many copies there are, which a set cannot answer.