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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With