1

First time posting here. I recently implemented Binary Search but sometimes my outputs will return a giant negative number instead. Now my first thought is that I'm printing a number where my pointer is pointing at a random memory location. Can someone help me with the logic and how I can improve my code?

#include <stdio.h>
#include <stdlib.h>

int binarysearch(int *array, int size, int target);

int main() {
    int array[] = { 1, 2, 3, 4, 5, 6 };
    printf("%d\n", binarysearch(array, 8, 15));
    return 0;
}

int binarysearch(int *array, int size, int target) {
    int mid;    
    mid = size / 2;

    if (size < 1) {
        return -1;
    }
    if (size == 1) {
        return array[0];
    }
    if (target == array[mid]) {
        return target;
    } else
    if (target < array[mid]) {
        binarysearch(array, mid, target);
    } else{
        binarysearch(array + mid, size - mid, target);
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Kelvin
  • 79
  • 1
  • 1
  • 2
  • 3
    Why do you call the function such a way binarysearch(array, 8, 15)); when the array has only 6 elements? – Vlad from Moscow Nov 15 '16 at 00:09
  • 2
    You can't (shouldn't — can't meaningfully) return `array[0]` just because there's a single element in the array; it might not be equal to the value you're looking for. And you need to return the value found by the recursive calls; you invoke undefined behaviour when the value that wasn't returned is used by the calling code. – Jonathan Leffler Nov 15 '16 at 00:10
  • @JonathanLeffler He can but he should not do that.:) – Vlad from Moscow Nov 15 '16 at 00:11
  • @VladfromMoscow: yeah — fair comment; addressed by revision. – Jonathan Leffler Nov 15 '16 at 00:12
  • Also, you need to think about what you are returning. Is it a position in the array (or `-1` if the target is not found), or are you returning the target value? The latter is generally a lot less useful; it tells you "yes, the value you are looking for is somewhere in the array", whereas returning the position allows you to do much more. – Jonathan Leffler Nov 15 '16 at 00:24
  • 1
    An alternative would be to use `bsearch` – RoadRunner Nov 15 '16 at 00:25
  • You need to change the recursive calls to return the result: `return binarysearch(...);` – Barmar Nov 15 '16 at 01:13
  • @Barmar OP will not find that duplicate answer helpful. – RoadRunner Nov 15 '16 at 01:28
  • @RoadRunner I also put a comment here with the specific change he needs to make. – Barmar Nov 15 '16 at 01:29
  • Hi, I was trying to return the closest number to the target in the array if target was not in the array – Kelvin Nov 15 '16 at 03:51

3 Answers3

3

For starters you call the function with an invalid number of elements in the array that has only 6 elements.

int array[] = { 1, 2, 3, 4, 5, 6 };
printf("%d\n", binarysearch(array, 8, 15));
                                  ^^^

Also this snippet

if (size == 1) {
    return array[0];
}

is incorrect. It is not necessary that the first element is equal to target.

This statement

    binarysearch(array + mid, size - mid, target);

has to be written like

    binarysearch(array + mid + 1, size - mid - 1, target);

And at last the function has undefined behavior because it returns nothing in these cases

if (target < array[mid]) {
    binarysearch(array, mid, target);
} else{
    binarysearch(array + mid, size - mid, target);
}

You need to write

if (target < array[mid]) {
    return binarysearch(array, mid, target);
} else{
    return binarysearch(array + mid, size - mid, target);
}

And two words about the programming style. It is better to name the function either like binary_search or like binarySearch or at last like BinarySearchthan like binarysearch.

In general it is not a good design of the function. Imagine that the array has an element with the value -1. How will you determine whether this element is present in the array or is absent?

Usually such functions return pointer to the target element in case if it is found or NULL pointer otherwise.

Here is a demonstrative program that shows how this approach can be implemented.

#include <stdio.h>

int * binary_search( const int *a, size_t n, int target ) 
{
    if ( n == 0 ) return NULL;

    size_t middle = n / 2;

    if ( a[middle] < target )
    {
        return binary_search( a + middle + 1, n - middle - 1, target );
    }
    else if ( target < a[middle] )
    {
        return binary_search( a, middle, target );
    }

    return a + middle;
}

int main(void) 
{
    int array[] = { 1, 2, 3, 4, 5, 6 };
    const size_t N = sizeof( array ) / sizeof( *array );

    for ( int i = 0; i < 8; i++ )
    {
        int *target = binary_search( array, N, i  );
        if ( target )
        {
            printf( "%d is found at position %d\n",  *target, ( int )(target - array ) );
        }
        else
        {
            printf( "%d is not found\n", i );
        }
    }

    return 0;
}

The program output is

0 is not found
1 is found at position 0
2 is found at position 1
3 is found at position 2
4 is found at position 3
5 is found at position 4
6 is found at position 5
7 is not found

By the way according to the C Standard function main without parameters shall be declared like

int main( void )
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • returning `binarysearch(array + mid, size - mid, target);` is incorrect: it is not the offset into the array. You must store the result, compare that to -1 and add `mid` if not equal. – chqrlie Nov 15 '16 at 01:01
  • @chqrlie He does not return the position. He returns the target.:) – Vlad from Moscow Nov 15 '16 at 01:04
  • He returns the target if found and -1 or some random value if not. It is unclear what the intended semantics are. returning the target is bogus anyway since -1 is a valid `int` that could not be searched for. Your proposed alternative API is OK, mine is simpler. – chqrlie Nov 15 '16 at 01:09
  • Hi, thanks for the help everyone. Vlad an you explain why you did binarysearch(array + mid + 1, size - mid - 1, target); instead? – Kelvin Nov 15 '16 at 03:42
  • @Kelvin Let assume that the array has only one element. Then middle is equal to 0.if a[0] < target and you call binarsearch( array + 0, size - 0, target ) then you will get an infinite recursion., – Vlad from Moscow Nov 15 '16 at 09:41
2

You call binarysearch(array, 8, 15)) but your array has only 6 entries.

Here is how to compute the proper size automatically:

int main(void) {
    int array[] = { 1, 2, 3, 4, 5, 6 };
    printf("%d\n", binarysearch(array, sizeof(array) / sizeof(array[0]), 15));
    return 0;
}

Note that your function binarysearch has problems too:

  1. Returning the array entry is bogus, what do you return if the target is less than the first entry? -1 is not necessarily less than the first entry.

  2. You are supposed to return the index into the array with the entry if found and -1 if not found.

  3. When you recurse, you do not return the value from these recursive calls: you should compile with warnings enabled (for example: gcc -Wall -W) and look at all the helpful diagnostic messages the compiler produces.

Here is a modified version:

int binarysearch(const int *array, int size, int target) {

    int a, b;

    for (a = 0, b = size; a < b;) {
        int mid = a + (b - a) / 2;
        if (target <= array[mid]) {
            b = mid;
        } else {
            a = mid + 1;
        }
    }
    // a is the offset where target is or should be inserted
    if (a < size && target == array[a])
        return a;
    else
        return -1;
}

Notes:

  • Computing mid = (a + b) / 2; would be potentially incorrect for large sizes as there may be an arithmetic overflow. mid = a + (b - a) / 2; does not have this problem since a < b.

  • The time-complexity is O(Log N), and for a given size, the function performs the same number of steps for all target values.

  • If the array contains multiple identical values equal to target, the index returned by binarysearch is that of the matching entry with the lowest index.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    This solves one problem; it doesn't solve several others. – Jonathan Leffler Nov 15 '16 at 00:25
  • @JonathanLeffler: I updated the answer. I believe it is complete now, uses fewer comparisons in the general case and handles duplicates correctly. – chqrlie Nov 15 '16 at 01:17
  • Code looks better. IIRC, the logic gives the lowest indexed entry when there are multiple matching entries. I'm a little puzzled about you distinction between "constant lookup time" and "O(log N) time complexity". What am I missing? – Jonathan Leffler Nov 15 '16 at 01:24
  • @JonathanLeffler: The function's time-complexity is logarithmic, and for a given N, it performs exactly the same number of steps. I shall update my wording. – chqrlie Nov 15 '16 at 01:27
  • Curious that code takes pre-cautions about integer overflow for array indexing, yet uses type `int` rather than type `size_t`, the type of the `sizeof(array) / sizeof(array[0])` calculation. – chux - Reinstate Monica Nov 15 '16 at 02:40
  • 1
    @chux: I thought about that... with the current API, it would be awkward to return `(size_t)-1` for not found. An alternate approach returning a pointer or a boolean would be more appropriate for very large arrays. The classic `(a + b) / 2` was worth mentioning. – chqrlie Nov 15 '16 at 15:30
1

You could make this problem easier by using the bsearch function offered by the <stdlib.h> library.

Something like this:

#include <stdio.h>
#include <stdlib.h>

int cmpfunc(const void * a, const void * b);

int
main(void) {
    int array[] = {1, 2, 3, 4, 5, 6};
    size_t n = sizeof(array)/sizeof(*array);
    int *item;
    int key = 15;

    item = bsearch(&key, array, n, sizeof(*array), cmpfunc);

    if (item != NULL) {
        printf("Found item  = %d\n", *item);
    } else {
        printf("Item = %d could not be found\n", key);
    }

    return 0;
}

int 
cmpfunc(const void * a, const void * b) {
   return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);
}

If you don't want to use bsearch, then this method will be fine also:

#include <stdio.h>
#include <stdlib.h>

#define BSFOUND 1
#define BS_NOT_FOUND 0

int cmpfunc(const void * a, const void * b);
int binary_search(int A[], int lo, int hi, int *key, int *locn);

int
main(void) {
    int array[] = {1, 2, 3, 4, 5, 6};
    size_t n = sizeof(array)/sizeof(*array);
    int key = 4, locn;

    if ((binary_search(array, 0, n, &key, &locn)) == BSFOUND) {
        printf("Found item = %d\n", array[locn]);
    } else {
        printf("Item = %d cound not be found\n", key);
    }

    return 0;
}

int
binary_search(int A[], int lo, int hi, int *key, int *locn) {
   int mid, outcome;

   if (lo >= hi) {
      return BS_NOT_FOUND;
   }

   mid = lo + (hi - lo) / 2;
   if ((outcome = cmpfunc(key, A+mid)) < 0) {
      return binary_search(A, lo, mid, key, locn);
   } else if(outcome > 0) {
      return binary_search(A, mid+1, hi, key, locn);
   } else {
      *locn = mid;
      return BSFOUND;
   }
}

int 
cmpfunc(const void * a, const void * b) {
   return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);
}
RoadRunner
  • 25,803
  • 6
  • 42
  • 75