I've tried to write a code that finds the first occurrence of a positive integer in a sorted array using binary search, but it doesn't work.
Here's the code:
def findFirstOccurrence(arr):
(left, right) = (0, len(arr) - 1)
result = -1
while left <= right:
mid = (left + right) // 2
if 0 < arr[mid]:
result = mid
right = mid - 1
elif 0 > arr[mid]:
right = mid - 1
else:
left = mid + 1
return result
Below code will help.
def findFirstOccurrence(arr):
left,right=0,len(arr)-1
while not((left==right) or (right==left+1)):
mid=(left+right)//2
if arr[mid]>0:
right=mid
else:
left=mid
if arr[left]>0:
return left
if arr[right]>0:
return right
return None
print(findFirstOccurrence([-4,-3,-2,-1,0,1,2,5,3,4,6]))
Output
5
The error comes from the lines
elif 0 > arr[mid]:
right = mid - 1
If arr[mid] is negative, we decrease the right pointer — i.e. we look further to the left. But if the array is sorted in ascending order, then anything further to the left of a negative number is still negative, so we'll never reach a positive number and the program will fail.
What we want to do instead is look to the right, where the positive numbers are. The lines
else:
left = mid + 1
already do that in the case that arr[mid] == 0. Removing the two erroneous lines would allow the case that arr[mid] < 0 to fall through and do what we want.
Final code:
if arr[mid] > 0:
result = mid
right = mid - 1
else:
left = mid + 1
Related
I am writing a binary search function that finds the minimum times that a given numeric list has been rotated. I believe my code is correct except for my elif statement. What needs to be changed in this code to make it work?
def count_rotations_binary(nums):
lo = 0
hi = len(nums)-1
while lo <= hi:
mid = (lo + hi) // 2
mid_number = nums[mid]
# Uncomment the next line for logging the values and fixing errors.
print("lo:", lo, ", hi:", hi, ", mid:", mid, ", mid_number:", mid_number)
if mid > 0 and mid_number < nums[mid-1]:
# The middle position is the answer
return mid
elif mid_number > nums[mid-1]:
# Answer lies in the left half
hi = mid - 1
else:
# Answer lies in the right half
lo = mid + 1
return
How can I get this to print all triplets that have a sum less than or equal to a target? Currently this returns triplets that are = to the target. I've tried to change and think but can't figure out
def triplets(nums):
# Sort array first
nums.sort()
output = []
# We use -2 because at this point the left and right pointers will be at same index
# For example [1,2,3,4,5] current index is 4 and left and right pointer will be at 5, so we know we cant have a triplet
# _ LR
for i in range(len(nums) - 2):
# check if current index and index -1 are same if same continue because we need distinct results
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while left < right:
currentSum = nums[i] + nums[left] + nums[right]
if currentSum <= 8:
output.append([nums[i], nums[left], nums[right]])
# below checks again to make sure index isnt same with adjacent index
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
# In this case we have to change both pointers since we found a solution
left += 1
right -= 1
elif currentSum > 8:
left += 1
else:
right -= 1
return output
So for example input array is [1,2,3,4,5] we will get the result (1,2,3),(1,2,4),(1,2,5),(1,3,4) Because these have a sum of less than or equal to target of 8.
The main barrier to small changes to your code to solve the new problem is that your original goal of outputting all distinct triplets with sum == target can be solved in O(n^2) time using two loops, as in your algorithm. The size of the output can be of size proportional to n^2, so this is optimal in a certain sense.
The problem of outputting all distinct triplets with sum <= target, cannot always be solved in O(n^2) time, since the output can have size proportional to n^3; for example, with an array nums = [1,2,...,n], target = n^2 + 1, the answer is all possible triples of elements. So your algorithm has to change in a way equivalent to adding a third loop.
One O(n^3) solution is shown below. Being a bit more clever about filtering duplicate elements (like using a hashmap and working with frequencies), this should be improvable to O(max(n^2, H)) where H is the size of your output.
def triplets(nums, target=8):
nums.sort()
output = set()
for i, first in enumerate(nums[:-2]):
if first * 3 > target:
break
# Filter some distinct results
if i + 3 < len(nums) and first == nums[i + 3]:
continue
for j, second in enumerate(nums[i + 1:], i + 1):
if first + 2 * second > target:
break
if j + 2 < len(nums) and second == nums[j + 2]:
continue
for k, third in enumerate(nums[j + 1:], j + 1):
if first + second + third > target:
break
if k + 1 < len(nums) and third == nums[k + 1]:
continue
output.add((first, second, third))
return list(map(list, output))
My for loop keeps stopping in main. It is supposed to call the def binarySearch 10000 times and add up the total number of guesses from each try. I notice when I lower the range down to 100, the for loop works MOST of the time. It still sometimes just freezes and never prints anything.
import random
def binarySearch(left, right, x):
counter = 0 #guesses per try
while left <= right:
counter+=1
mid = (right + left) // 2
if x == mid:
return counter
elif x > mid:
left = mid
elif x < mid:
right = mid
def main():
guesses = 0 # total number of guesses
###Problem here###
for x in range(10001):
lef = 0 #min
rig = 1000 #max
num = random.randint(lef, rig) #random number
guesses += binarySearch(lef, rig, num) #calls binarySearch and sums the total guesses
print("Total guesses from 10000 tries: ", guesses)
main()
EDIT:
I narrowed down the problem to:
elif x < mid:
left = mid
I tested it quite a few times and came to the conclusion that the while loop ends up getting stuck repeating that else if statement. I do not know why this is happening though.
The reason it is getting stuck is because there is an error in the boundary condition. If the number is not equal to mid and the left and right should be equal to mid + 1 and mid - 1 respectively. No need to consider mid again. Since you are considering mid again and again, your condition is never coming out of this cycle and hence the infinite loop.
elif x > mid:
left = mid + 1
elif x < mid:
right = mid - 1
These should be your new values for left and right. No need to consider mid again because it is not equal to the target value, if it were, the top most if the condition would have been true.
So I understand conceptually how binary search works, but I always have problems with implementing it when trying to find an index in an array. For instance, for the Search Insert Position on LC, this is what I wrote:
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if target > nums[-1]:
return len(nums)
low = 0
high = len(nums) - 1
while high > low:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid
else:
low = mid + 1
return low
It works, but I don't understand why I have to update low as mid + 1 instead of updating low as mid. Similarly, why am I updating high as mid instead of mid - 1. I've tried updating low/high as every combination of mid, mid - 1, and mid + 1 and the above is the only one that works but I have no idea why.
When implementing binary search for these kinds of problems, is there a way to reason through how you update the low/high values?
This is personal favorite:
while high >= low:
mid = (low + high) // 2
if nums[mid] >= target:
high = mid - 1
else:
low = mid + 1
return low
# or return nums[low] == target for boolean
It has difference in the case that has same values.
for example, Let's assume the array is [1,1,2,2,3,3,3,3,4].
with your function, search(arr, 1) returned 1 BUT search(arr, 2) returned 2.
why does it returned most RIGHT index on the interval 1s, and returned most LEFT index on 2s?
As i think, the key is at if nums[mid] >= target:.
when it finds the target exactly same one, the range changes by high = mid - 1. it means high might not be answer because the answer we found is mid. [1]
At the last step of binary search, the range is going to close to zero. and finally loop breaks by they crossed. thus, the answer must be low or high. but we know high is not an answer at [1].
I am trying to change my code so instead of finding a specific value of the array it will output the value of an interval if found, example being 60-70. Any help is appreciated.
def binary (array, value):
while len(array)!= 0:
mid = len(array) // 2
if value == array[mid]:
return value
elif value > array[mid]:
array = array[mid+1:]
elif value < array [mid]:
array = array[0:mid]
sequence = [1,2,5,9,13,42,69,123,256]
print( "found", binary(sequence,70) )
I have this so far and want it to find an specified interval, so if i specify 60-70 it will find what is in between.
Actually this is pretty simple:
While searching for the elements in the interval (lower, upper), perform a binary search on the array arr for the index of the smallest element arr[n], such that arr[n] >= lower and the index of the largest element arr[m], such that arr[m] <= upper.
Now there are several possibilities:
n < m: there exist multiple solutions in the array. All of the are in the subarray starting at index n up to index m inclusively
n = m: there exists precisely one solution: arr[n]
n > m: no solutions exist
Searching for values beyond a certain threshold can be done using binary search like this:
def lowestGreaterThan(arr, threshold):
low = 0
high = len(arr)
while low < high:
mid = math.floor((low + high) / 2)
print("low = ", low, " mid = ", mid, " high = ", high)
if arr[mid] == threshold:
return mid
elif arr[mid] < threshold and mid != low:
low = mid
elif arr[mid] > threshold and mid != high:
high = mid
else:
# terminate with index pointing to the first element greater than low
high = low = low + 1
return low
Sorry bout the looks of the code, my python is far from perfect. Anyways, this ought to show the basic idea behind the approach. The algorithm basically searches for the index ind of the first element in the array with the property arr[ind] >= threshold.