You are on page 1of 2

Given an array of integers 

nums  which is sorted in ascending order, and an integer  target , write a


function to search  target  in  nums . If  target  exists, then return its index. Otherwise, return  -1 .
You must write an algorithm with  O(log n)  runtime complexity.
Example 1:
Input: nums = [-1,0,3,5,9,12], target = 9 Output: 4 Explanation: 9 exists in nums and its
index is 4
Example 2:
Input: nums = [-1,0,3,5,9,12], target = 2 Output: -1 Explanation: 2 does not exist in nums
so return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:

Learnings
Use // for integer division in python.
Reviewed binary search.
Some questions I would ask right off the bat:
Are there duplicates?
What is the pre and post condition? I know that the loop invariant is best given as a diagram for this one.
Actually...I'd like to meditate for a bit on the general principle behind binary search. I want to say that binary
search only works on a monotonic function. But for some reason I remember Elliot saying that that isn't
quite right, and I'm trying to remember why...
The general idea is to start with an interval defined by low and high in the domain. You are searching for
an x such that low <= x <= high and f(x) == target . Yes, it seems to me that binary search only
works if f is monotonic.
Q: nums is an array of integers
R:
P: nums[low] < target and nums[high] >= target
B: high-low > 1

In binary search, we are trying to find *the first* element that satisfies a
predicate. In this case, our predicate is (>= target).
Imagine a sequence Falses followed by a sequence of Trues.
We want to find the first True.

I believe the invariant is that pred(f(low)) == False and pred(f(high)) == True.


Then you shrink high-low until you hit an interval size you're comfortable searching
for/reading off the answer.

The only problem is that I'm having trouble coming up with the commands to initialize
the invariant.
How do I guarantee that nums[low] >= 0 == False in the beginning?
If I use low := 0, but the target we are searching for is nums[0], then the invariant
doesn't hold...
Perhaps I have the wrong predicate?
How do I want to handle empty arrays??

I am tempted to use low := -1 as initialization...


I'm just worried about a possible index out of bounds if we access nums[low] in that
state.
I am also tempted to use high := len(nums).
We could also use a different predicate: pred x = (> x).
This way, low can be initialized as 0 and high to len(nums).
This also means that high-low == number of elements in range(low,high).
So the guard becomes easy top: high-low != 1.

class Solution:
def search(self, nums: List[int], target: int) -> int:
# Define nums[len(nums)] > target (a little uncomfortable with this)
low,high = 0,len(nums)
# P: nums[low] <= target and nums[high] > target
while high-low > 1:
mid = (low+high)//2
if nums[mid] <= target:
low = mid
else:
high = mid
# R: high-low <= 1 and nums[low] <= target and nums[high] > target
if high-low == 0 or nums[low] != target:
return -1
else:
return low

You might also like