The explanation says that “The peak element is always larger than the next element. Applying the filter of arr[i] > arr[i + 1] we get a boolean array”.
A similar alternative approach could follow the following explanation:
The peak element is always larger than the previous element. Applying the filter of arr[i] > arr[i-1] we get a boolean array. We now search for the first False element.
I did that but for some reason my code fails on test-case [0 10 3 2 1 0]
# WRITE YOUR BRILLIANT CODE HERE
start, end, ans = 0, len(arr)-1, -1
while start<=end:
mid = (start+end)//2
if arr[mid] > arr[mid-1]:
ans = mid
start = mid+1
else:
end = mid-1
return ans```
The problem with this method is when mid is 0, then it’d compare to mid - 1 = -1. In the above case it’s equal, not greater and so it goes into the else path which is incorrect. The assumption should be there is an -1 position that is -inf. We can do that by adding mid == 0 in the if condition.
start, end, ans = 0, len(arr)-1, -1
while start<=end:
mid = (start+end)//2
if mid == 0 or arr[mid] > arr[mid-1]: # mid == 0
ans = mid
start = mid+1
else:
end = mid-1
return ans
Where are we handling the case of the last element being the peak element?
Traceback (most recent call last):
File “solution.py”, line 19, in
res = peak_of_mountain_array(arr)
File “solution.py”, line 9, in peak_of_mountain_array
if arr[mid] > arr[mid + 1]:
IndexError: list index out of range
Clunky and possibly slower, but this is what I came up with before looking at the explanation:
def peak_of_mountain_array(arr: List[int]) -> int:
left, right = 0, len(arr) - 1
max = 0
max_index = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] > max:
if mid > max_index:
left = mid + 1
else:
right = mid - 1
max_index = mid
max= arr[max_index]
else:
if mid > max_index:
right = mid - 1
left=0
else:
left = mid + 1
return max_index
The idea being that if you’re to the right of the previous max and the current value is less than previous max, the peak is somewhere on your left so you should throw away everything to your right but also check possibly missed search space. I was intentionally trying to avoid looking up array values for some reason, would be interesting to compare this version against algomonster’s version for speed.
Here is my solution:
public static int peakOfMountainArray(List arr) {
// WRITE YOUR BRILLIANT CODE HERE
int left = 0;
int right = arr.size() - 1;
int mid = 0;
while (left <= right) {
mid = left + (right - left) / 2;
if (arr.get(mid) > arr.get(mid + 1)) {
if(arr.get(mid) > arr.get(mid - 1)) {
return mid;
} else {
right = mid - 1;
}
} else {
left = mid + 1;
}
}
return mid;
}
Check this out tho… It is no different than canonical binary search, except the target definition. Instead of a static value, target is dynamic - it’s whatever value is greater than both its neighbors. So we can just get by with a small modification:
def peak_of_mountain_array(arr: List[int]) -> int:
l, r = 1, len(arr) - 2
while l <= r:
mid = (l + r) // 2
a, b, c = arr[mid - 1 : mid + 2]
if a < b > c:
return mid
elif a < b < c:
l = mid + 1
else:
r = mid - 1
return mid
Why is right boundary not handled? Is it because we will never reach mid == len(arr) - 1 case because peak cannot be the last element and len(arr) >= 3