Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am trying to tackle a classical interview problem which is basically performing a binary search on a list which first increases then decreases. Even though it's obvious that we can achieve O(log n) I couldn't figure out what is wrong the code below that I've written:

#include <iostream>

using namespace std;


int binarySearch(int *A, int low, int high, int key)
{
    while(low < high)
    {
        int mid = (low + high) / 2;
        if(key < A[mid])
        {
            if(A[mid - 1] < A[mid] && A[mid] < A[mid + 1])
                high = mid - 1;
            else
                low = mid + 1;
        }
        else if(key > A[mid])
        {
            if(A[mid - 1] < A[mid] && A[mid] < A[mid + 1])
                low = mid + 1;
            else
                high = mid - 1;
        }
        else
            return mid;
    }

    return -(low + 1);
}


int main()
{
    const int SIZE = 8;
    int A[SIZE] = {3,5,7,14,9,8,2,1};
    cout<<binarySearch(A, 0, SIZE, 14);
    return 0;
}

The reason why I ask this question is because I wonder two things. 1) What is wrong with the code since it fails for some values such as "14". 2) Can it be improved?

share|improve this question
 
You also have an unchecked integer overflow in (low + high) / 2. –  Nathan Ernst Jun 27 '13 at 19:36
add comment

3 Answers

I think your code does not handle well the increasing and decreasing part of the array.

Instead of telling you exactly how to do this, here is some tip and I hope you are able to finish it :)

One solution is to first find the point where the array goes from increasing order to decreasing order in O(logn), then based on that, perform a special version of binary search in O(logn).

Let me know in case you don't know how to to this, I will explain more on my answer.

share|improve this answer
 
Thank you for the tip! –  Ali Jun 27 '13 at 19:19
 
The only way I can think to find the decreasing point is to divide the array in to two-sized arrays and compare for each set if it validates A[i] < A[i + 1]. But I could not think up the rest. –  Ali Jun 27 '13 at 19:30
 
added another tip :) –  keelar Jun 27 '13 at 19:32
add comment

Here is a more complete solution (served only as a reference)

One solution is to first find the point where the array goes from increasing order to decreasing order in O(logn), then based on that, perform a special version of binary search in O(logn).

The first part can be achieved by finding the last point where array[i-1] <= array[i] using a specialized binary search where the mid index moving condition is whether array[mid-1] <= array[mid] instead of whether array[mid] <= target.


Sample Code

To prevent me from being an interview hacker, below only shows how to handle an array without any duplicates. The code will soon removed if necessary:

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

int find_change_point(int array[], int low, int high, int array_size) {
  int mid;
  for (mid = (low + high) / 2; low <= high; mid = (low + high) / 2) {
    // if true, it implies the point is on the higher side
    if (array[mid - 1] <= array[mid]) {
      if (mid == array_size - 1) {
        return mid;
      }
      // since we already handles equality, only > is needed.
      if (array[mid] > array[mid + 1]) {
        return mid;
      }
      low = mid + 1;
    } else {
      // then simply imply the point is on the lower part
      high = mid - 1;
    }
  }
  return mid;
}

int main() {
  const int SIZE_1 = 8;
  int array_1[SIZE_1] = {3,5,7,14,9,8,2,1};
  int change_point = find_change_point(array_1, 0, SIZE_1 - 1, SIZE_1);
  printf("change_point_1 = %d\n", change_point);

  const int SIZE_2 = 9;
  int array_2[SIZE_2] = {3,5,7,14,15,16,17,19, 20};
  change_point = find_change_point(array_2, 0, SIZE_2 - 1, SIZE_2);
  printf("change_point_2 = %d\n", change_point);

  const int SIZE_3 = 9;
  int array_3[SIZE_3] = {9, 8, 7, 6, 5, 4, 3, 2, 1};
  change_point = find_change_point(array_3, 0, SIZE_3 - 1, SIZE_3);
  printf("change_point_3 = %d\n", change_point);
}

and the output is:

change_point_1 = 3
change_point_2 = 8
change_point_3 = 0

For handling duplications, you need to find its left-end and right-end of the duplication sequence and verify whether they are increasing or decreasing sequence.

The second part has many varieties. One simple solution is to treat them as two arrays, and perform one binary search for each subarray to find your target element.

share|improve this answer
add comment

For your code i think to many branch like "if" operation . Following is simple pseudocode for your reference:

while(1 < (high - low)){
     int mid = (low + high) >> 1;
     (key < A[mid]) ? high = mid : lo = mid;
}
return (key == A[lo]) ? lo : -1;

Hope this can be help you.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.