Monday 8 July 2013

Given a sorted rotated array, find an element in O(logn) time without finding the pivot element.


//============================================================================
// Name        : SortedRotatedArray.cpp
// Author      : Varun Sharma
// Version     : 1.0
// Complexity  : O(logn)
// Description : Finding Element in a sorted rotated array, without finding pivot.
//============================================================================
/*
ALGORITHM
=========
1.  Initialize start = 0, end = size-1.
2.  Repeat while start <= end
    a.  Calculate mid element =>  mid = (start+end)/2.
    b.  If arr[mid] = val then print position and return true.
    c.  Array is sorted from start to mid or from mid to end, check which one is the case.
    d.  Sorted from Mid to end: arr[mid] < arr[end].
        i. If search value lies in right sorted half : val arr[mid] && val <= arr[end] 
           then start = mid+1
        ii.Else end = mid-1 
    e.  Else Sorted from start to mid .
        i. If search value lies in left sorted half: val <arr[mid] && val>= arr[start]
           then end = mid-1
        ii.Else start = mid+1.
3.  Return false if element not found.
*/

#include <iostream>
using namespace std;

bool search(int arr[],int size,int val) {
      int start = 0;
      int end = size-1;
      int mid;
      while(start <= end) {
            mid = (start+end)/2;

            //Value at mid == search value then return found.
            if(arr[mid] == val) {
                  cout<<"nFOUND AT POSITION : "<<mid+1;
                  return true;
            }

            //If value at mid < value at end
            //i.e. Array is sorted from mid to end.
            if(arr[mid] < arr[end]) {
                  //Value lies in right sorted half of array.
                  if(val > arr[mid] && val <= arr[end])
                        start = mid+1;
                  else
                  //Value lies in left half of array.
                        end = mid-1;
            }
            //Array is sorted from start to mid.
            else {
                  //Value lies in left sorted half of array.
                  if(val < arr[mid] && val >= arr[start])
                        end = mid-1;
                  //Value lies in right sorted half.
                  else
                        start = mid+1;
            }
      }
      return false;
}

int main() {
      int arr[] = {6,7,8,9,10,11,1,2,3,4,5};
      int size = sizeof arr/sizeof arr[0];
      if(!search(arr,size,6))
            cout<<"nVALUE DOESN'T EXIST";

      int arr1[] = {6,7,8,9,10,11,12,13,14,5};
      int size1 = sizeof arr1/sizeof arr1[0];
      if(!search(arr1,size1,5))
                  cout<<"nVALUE DOESN'T EXIST";

      int arr2[] = {5,6,7,8,9,10,11,12,13,14};
      int size2 = sizeof arr2/sizeof arr2[0];
      if(!search(arr2,size2,5))
                  cout<<"nVALUE DOESN'T EXIST";

      int arr3[] = {5,6,7,8,9,10,11,12,13,14};
      int size3 = sizeof arr3/sizeof arr3[0];
      if(!search(arr3,size3,18))
                  cout<<"nVALUE DOESN'T EXIST";

      int arr4[] = {6,7,8,9,10,11,12,13,14,5};
      int size4 = sizeof arr4/sizeof arr4[0];
      if(!search(arr4,size4,1))
                  cout<<"nVALUE DOESN'T EXIST";

      return 0;
}


10 comments:

  1. It does not work for the following array.

    {2,3,4,5,6,1}, Search element = 5

    First iteration:
    mid = 4(assuming start = 0 and end = 5).
    arr[mid] < search_elem and arr[end] < search_elem,
    so end = mid - 1=1;

    So, search space is reduced to {0,1}, which is wrong.

    ReplyDelete
  2. Sorry for not properly tested code, btw nice catch soumya.
    ok, at first glance issue is:
    I have assumed that pivot element will be on left of mid element so array will be sorted from mid to end (which is not true always).

    We can fix this bug by checking if pivot lies in first half ( < mid) or if pivot lies in second half (>mid) it is O(1) operation.

    If pivot lies in first half above code will work as array will be sorted from mid to end, but if pivot lies in second half then array will be sorted from start to mid, so instead of comparing mid element with end element compare it with start element and accordingly move end/start indexes.

    ReplyDelete
  3. I have posted the new code and algorithm.
    I have tested for couple of test cases but too lazy to test it for all, please let me know in case you find any bug.

    ReplyDelete
  4. it won't work with array {2,1,0,8,7,6,5,4,3} search for 0

    ReplyDelete
  5. Charly, the array you have used as input is not in increasing order.
    Sorry my mistake I should have mentioned array is sorted in Increasing order and it is circular shifted from left to right.

    Your array must look something like this: {3,4,5,6,7,8,0,1,2};
    And code works fine for the above array.

    ReplyDelete
  6. Which mean your solution will work only on sorted array not in reverse sorted one

    ReplyDelete
  7. Yes, like sorting function can sort in both ascending and descending order with little change this can be extended to work with reverse sorted array as well.

    ReplyDelete
  8. One elegant solution could be to find the minimal number in the rotated sorted index t s.t. a[0] <= a[1] <= ...... <=a[t-1] >= a[t] and a[t] <= a[t+1]... a[n-1] and a[n-1] <= a[0] <=...<=a[t-1], this can be found in O(Log n) time with some twist of the divide and conquer. Once t is found we can do a binsearch in left and right of t and check for the target number. off course we also check if t itself is the target .

    ReplyDelete
  9. Have written the code and tested it , don't know if publishing here in comment is proper. Neways .

    ReplyDelete
  10. //Find target in a rotated sorted array i.e. there exists index t s.t. a[0] <= a[1] <= ...... <=a[t-1] >= a[t] and a[t] <= a[t+1]... a[n-1] and
    // a[n-1] <= a[0] <=...<=a[t-1]

    public static int FindTargetInRotatedSortedArray(int[] a, int target)
    {
    //First lets us get the index t i.e. the least element of the array note t-1th element is the max element . this would simplify things

    // To do so we use the concept of bnary search divide and conquer

    int low = 0, high = a.Length - 1; int mid; int t;

    while(low <= high)
    {
    mid = (low + high) / 2;

    if (a[mid] == target)
    return mid;

    if ((a[mid] <= a[high]) && (a[mid] <= a[low])) // indicates that mid is t i.e. the least element in the array
    return FindInSplittedArray(a, target, mid); // So the target can potentially be in either of the sorted arrays 0... to t-1 or t+1 to ... n-1

    else if ((a[mid] >= a[high]) && (a[mid] >= a[low])) // least element is in the right of mid
    low = mid + 1;

    else if ((a[mid] <= a[high]) && (a[mid] <= a[low])) // least element is in the left of mid
    high = mid - 1;

    else // This means a[mid ] > a[low] and a[mid] < a[high] , this is a case for normal sorted array
    high = mid - 1;
    }
    return -2; // This means that the array is not rotated sorted as we were not able to find the least element



    }
    //Array a is rotated sorted array with least element at index t
    private static int FindInSplittedArray(int[] a, int target, int t)
    {
    int result = binSearch(a, target, 0, t - 1);

    if (result != -1)
    return result;

    return binSearch(a, target, t + 1, a.Length - 1);
    }
    //Array of sorted integers in the given range of low to high indices . returns index if the target is in te given range low is <= high
    public static int binSearch(int[] a, int target, int low , int high)
    {


    if ((low < 0) || (low > a.Length - 1) || (high < 0) || (high > a.Length - 1))
    return -1;


    int mid;

    while (low <= high)
    {
    mid = (low + high) / 2;

    if (a[mid] == target)
    return mid;

    else if (a[mid ] > target)
    high = mid - 1;


    else
    low = mid + 1;
    }
    return -1;

    ReplyDelete