1

I want to implement a binary search function which returns the lowest index of the element to be searched. Here is my code:

def binarySearch(arr,x):
    n=len(arr)
    if n==1:
        if arr[0]==x:
            return 0
        else:
            return -1 # not in list
    else:
        m=int(n/2)
        if x <= arr[m]:
            return binarySearch(arr[:m],x)
        else:
            return m+binarySearch(arr[m+1:],x)

However, this doesn't work correctly. Can someone help me?

Tagc
  • 8,736
  • 7
  • 61
  • 114
S Hristoskov
  • 53
  • 1
  • 9

3 Answers3

1
def binarySearch(arr,x):

    if len(arr) == 0:
        return 0

    else:
        m=int(len(arr)/2)

        if arr[m] == x:
            c = 1

            while arr[m-c] == x:
                c += 1
            return m-c+1

        else:
            if x < arr[m]:
                return binarySearch(arr[:m],x)
            else:
                return binarySearch(arr[m+1:],x)

This fixes your issues while also giving you the lowest index

  • Special casing `arr[m-1]` won't work in the general case. `binarySearch([1,2,3,3,3,3,3,4,4], 3)` gives `3` instead of `2`. – wildwilhelm Jan 10 '17 at 17:34
  • Does that fix it? I have to get back to work and that's the quickest hack I can think of. – Fruitspunchsamurai Jan 10 '17 at 17:40
  • Nice, that looks better to me. Runtime is no longer logarithmic, tho I'm not sure if that can be guaranteed given OP's question. – wildwilhelm Jan 10 '17 at 17:44
  • yeah , its a good solution , but as wildwilhelm mentioned ,it isn't in log(n) :/ – S Hristoskov Jan 10 '17 at 17:48
  • [This](http://stackoverflow.com/questions/10148849/find-lowest-index-of-a-given-value-in-a-presorted-array) seems to be a good solution to your problem. The array is edited to sections that are likely to contain the value. With the array [1,3,3,3,3,3,3,4,4], my solution takes 0.0001163482666015625 while his takes 6.198883056640625e-06. – Fruitspunchsamurai Jan 10 '17 at 18:53
  • but I'm asked for an implemention of algorithm in log(n) – S Hristoskov Jan 10 '17 at 19:05
  • The selected answer for the question is log(n). – Fruitspunchsamurai Jan 10 '17 at 19:08
  • The solution posted by the OP in the thread is similar to mine, where it becomes roughly O(n) at the worst case. The selected answer improves on it by changing the binary search itself. – Fruitspunchsamurai Jan 10 '17 at 19:10
1

Time Complexity: O(log(n))
Space Complexity: O(1)

def index_equals_value_search(arr):

  left, right = 0, len(arr)-1
  lowest = -float('inf')
  while left<=right:   

    mid = (left+right)//2   
    print(mid, left, right, arr[mid])
    if arr[mid] == mid:     
      lowest = min(lowest, mid)
      right = mid-1
    elif arr[mid]<0:
       left = mid+1
    elif arr[mid]<mid:
      left = mid+1
    elif arr[mid]>mid:
      right = mid-1


  if lowest == -float('inf'): 
    return -1
  return lowest


arr = [-6,-5,-4,-1,1,3,5,7]
index_equals_value_search(arr)
Jai
  • 3,211
  • 2
  • 17
  • 26
0

You can find the index of an element which is equal to x by just adding a test for equality into the else part of your function:

def binarySearch(arr,x):
    n=len(arr)
    if n==1:
        if arr[0]==x:
            return 0
        else:
            return -1 # not in list
    else:
        m = int(n/2)
        if x < arr[m]:
            return binarySearch(arr[:m],x)
        elif x == arr[m]:
            return m
        else:
            return m + binarySearch(arr[m+1:],x)

This prevents the problem of recursing past the solution, mentioned by @Fruitpunchsalami

However, this won't get you the lowest index:

>>> binarySearch([1,2,3,3,4,4], 3)
3

Here the right answer would be 2.

A further problem is the handling of elements which are not found, due to the special case of -1. We get:

>>> binarySearch([1,2,3,3,6,6], 4)
2

I would be tempted to suggest a generic solution whereby you find the index of the largest element less than x, and then check the element in the position one up from that one.

Finding the largest element less than x can be done in logarithmic time; checking the element to the right is constant time, so you still get O(log n):

def binarySearch(arr,x):
    '''Returns the lowest index of the element equal to `x` or NaN if not found.'''
    def innerBinarySearch(arr, x):
        '''Find index of largest element strictly less than `x`.'''
        if len(arr) == 0:
            return -1
        m = len(arr) // 2
        if x <= arr[m]:
            return innerBinarySearch(arr[:m], x)
        else:
            return m + 1 + innerBinarySearch(arr[m + 1:], x)

    idx = innerBinarySearch(arr,x) + 1
    if 0 <= idx < len(arr) and arr[idx] == x:
        return idx
    return float('nan')

Do it all in one function:

def binarySearch(arr,x):
    '''Returns the lowest index of the element equal to `x` or NaN if not found.'''
    if len(arr) == 0:
        return float('nan')
    m = len(arr) // 2
    if arr[m] < x:
        return m + 1 + binarySearch(arr[m + 1:], x)
    elif x < arr[m] or (0 < m and arr[m-1] == x):
        return binarySearch(arr[:m], x)
    else:
        return m
wildwilhelm
  • 4,809
  • 1
  • 19
  • 24