Searching in a sorted and rotated array

C++CArraysAlgorithm

C++ Problem Overview


While preparing for an interview I stumbled upon this interesting question:

> You've been given an array that is sorted and then rotated. > > For example: > > * Let arr = [1,2,3,4,5], which is sorted > * Rotate it twice to the right to give [4,5,1,2,3]. > > > Now how best can one search in this sorted + rotated array?

One can unrotate the array and then do a binary search. But that is no better than doing a linear search in the input array, as both are worst-case O(N).

Please provide some pointers. I've googled a lot on special algorithms for this but couldn't find any.

I understand C and C++.

C++ Solutions


Solution 1 - C++

This can be done in O(logN) using a slightly modified binary search.

The interesting property of a sorted + rotated array is that when you divide it into two halves, atleast one of the two halves will always be sorted.

Let input array arr = [4,5,6,7,8,9,1,2,3]
number of elements  = 9
mid index = (0+8)/2 = 4

[4,5,6,7,8,9,1,2,3]
         ^
 left   mid  right

as seem right sub-array is not sorted while left sub-array is sorted.

If mid happens to be the point of rotation them both left and right sub-arrays will be sorted.

[6,7,8,9,1,2,3,4,5]
         ^

But in any case one half(sub-array) must be sorted.

We can easily know which half is sorted by comparing start and end element of each half.

Once we find which half is sorted we can see if the key is present in that half - simple comparison with the extremes.

If the key is present in that half we recursively call the function on that half
else we recursively call our search on the other half.

We are discarding one half of the array in each call which makes this algorithm O(logN).

Pseudo code:

function search( arr[], key, low, high)

        mid = (low + high) / 2

        // key not present
        if(low > high)
                return -1
          
        // key found
        if(arr[mid] == key)
                return mid
          
        // if left half is sorted.
        if(arr[low] <= arr[mid])

                // if key is present in left half.
                if (arr[low] <= key && arr[mid] >= key) 
                        return search(arr,key,low,mid-1)

                // if key is not present in left half..search right half.
                else                 
                        return search(arr,key,mid+1,high)
                end-if

        // if right half is sorted. 
        else    
                // if key is present in right half.
                if(arr[mid] <= key && arr[high] >= key) 
                        return search(arr,key,mid+1,high)

                // if key is not present in right half..search in left half.
                else
                        return search(arr,key,low,mid-1)
                end-if
        end-if  

end-function

The key here is that one sub-array will always be sorted, using which we can discard one half of the array.

Solution 2 - C++

The accepted answer has a bug when there are duplicate elements in the array. For example, arr = {2,3,2,2,2} and 3 is what we are looking for. Then the program in the accepted answer will return -1 instead of 1.

This interview question is discussed in detail in the book 'Cracking the Coding Interview'. The condition of duplicate elements is specially discussed in that book. Since the op said in a comment that array elements can be anything, I am giving my solution as pseudo code in below:

function search( arr[], key, low, high)

    if(low > high)
        return -1
    
    mid = (low + high) / 2
    
    if(arr[mid] == key)
        return mid

    // if the left half is sorted.
    if(arr[low] < arr[mid]) {

        // if key is in the left half
        if (arr[low] <= key && key <= arr[mid]) 
            // search the left half
            return search(arr,key,low,mid-1)
        else
            // search the right half                 
            return search(arr,key,mid+1,high)
        end-if

    // if the right half is sorted. 
    else if(arr[mid] < arr[high])    
        // if the key is in the right half.
        if(arr[mid] <= key && arr[high] >= key) 
            return search(arr,key,mid+1,high)
        else
            return search(arr,key,low,mid-1)
        end-if
   
    else if(arr[mid] == arr[low])
       
        if(arr[mid] != arr[high])
            // Then elements in left half must be identical. 
            // Because if not, then it's impossible to have either arr[mid] < arr[high] or arr[mid] > arr[high]
            // Then we only need to search the right half.
            return search(arr, mid+1, high, key)
        else 
            // arr[low] = arr[mid] = arr[high], we have to search both halves.
            result = search(arr, low, mid-1, key)
            if(result == -1)
                return search(arr, mid+1, high, key)
            else
                return result
   end-if
end-function

Solution 3 - C++

You can do 2 binary searches: first to find the index i such that arr[i] > arr[i+1].

Apparently, (arr\[1], arr[2], ..., arr[i]) and (arr[i+1], arr[i+2], ..., arr[n]) are both sorted arrays.

Then if arr[1] <= x <= arr[i], you do binary search at the first array, else at the second.

The complexity O(logN)

EDIT: the code.

Solution 4 - C++

My first attempt would be to find using binary search the number of rotations applied - this can be done by finding the index n where a[n] > a[n + 1] using the usual binary search mechanism. Then do a regular binary search while rotating all indexes per shift found.

Solution 5 - C++

int rotated_binary_search(int A[], int N, int key) {
  int L = 0;
  int R = N - 1;
 
  while (L <= R) {
    // Avoid overflow, same as M=(L+R)/2
    int M = L + ((R - L) / 2);
    if (A[M] == key) return M;
 
    // the bottom half is sorted
    if (A[L] <= A[M]) {
      if (A[L] <= key && key < A[M])
        R = M - 1;
      else
        L = M + 1;
    }
    // the upper half is sorted
    else {
      if (A[M] < key && key <= A[R])
        L = M + 1;
      else
        R = M - 1;
    }
  }
  return -1;
}

Solution 6 - C++

If you know that the array has been rotated s to the right, you can simply do a binary search shifted s to the right. This is O(lg N)

By this, I mean, initialize the left limit to s and the right to (s-1) mod N, and do a binary search between these, taking a bit of care to work in the correct area.

If you don't know how much the array has been rotated by, you can determine how big the rotation is using a binary search, which is O(lg N), then do a shifted binary search, O(lg N), a grand total of O(lg N) still.

Solution 7 - C++

Reply for the above mentioned post "This interview question is discussed in detail in the book 'Cracking the Coding Interview'. The condition of duplicate elements is specially discussed in that book. Since the op said in comment that array elements can be anything, I am giving my solution as pseudo code in below:"

Your solution is O(n) !! (The last if condition where you check both halves of the array for a single condition makes it a sol of linear time complexity )

I am better off doing a linear search than getting stuck in a maze of bugs and segmentation faults during a coding round.

I dont think there is a better solution than O(n) for a search in a rotated sorted array (with duplicates)

Solution 8 - C++

If you know how (far) it was rotated you can still do a binary search.

The trick is that you get two levels of indices: you do the b.s. in a virtual 0..n-1 range and then un-rotate them when actually looking up a value.

Solution 9 - C++

You don't need to rotate the array first. You can use binary search on the rotated array (with some modifications).

Let N be the number you are searching for:

Read the first number (arr[start]) and the number in the middle of the array (arr[end]):

  • if arr[start] > arr[end] --> the first half is not sorted but the second half is sorted:

    • if arr[end] > N --> the number is in index: (middle + N - arr[end])

    • if N repeat the search on the first part of the array (see end to be the middle of the first half of the array etc.)

(the same if the first part is sorted but the second one isn't)

Solution 10 - C++

short mod_binary_search( int m, int *arr, short start, short end)
{

 if(start <= end)
 {
	short mid = (start+end)/2;

	if( m == arr[mid])
		return mid;
	else
	{
		//First half is sorted
		if(arr[start] <= arr[mid])
		{
			if(m < arr[mid] && m >= arr[start])
				return mod_binary_search( m, arr, start, mid-1);
			return mod_binary_search( m, arr, mid+1, end);
		}

		//Second half is sorted
		else
		{
			if(m > arr[mid] && m < arr[start])
				return mod_binary_search( m, arr, mid+1, end);
			return mod_binary_search( m, arr, start, mid-1);
		}
	}
 }
 return -1;
}

Solution 11 - C++

First, you need to find the shift constant, k. This can be done in O(lgN) time. From the constant shift k, you can easily find the element you're looking for using a binary search with the constant k. The augmented binary search also takes O(lgN) time The total run time is O(lgN + lgN) = O(lgN)

To find the constant shift, k. You just have to look for the minimum value in the array. The index of the minimum value of the array tells you the constant shift. Consider the sorted array [1,2,3,4,5].

The possible shifts are:
[1,2,3,4,5] // k = 0
[5,1,2,3,4] // k = 1
[4,5,1,2,3] // k = 2
[3,4,5,1,2] // k = 3
[2,3,4,5,1] // k = 4
[1,2,3,4,5] // k = 5%5 = 0

To do any algorithm in O(lgN) time, the key is to always find ways to divide the problem by half. Once doing so, the rest of the implementation details is easy

Below is the code in C++ for the algorithm

// This implementation takes O(logN) time
// This function returns the amount of shift of the sorted array, which is
// equivalent to the index of the minimum element of the shifted sorted array. 
#include <vector> 
#include <iostream> 
using namespace std; 

int binarySearchFindK(vector<int>& nums, int begin, int end)
{
    int mid = ((end + begin)/2); 
    // Base cases
    if((mid > begin && nums[mid] < nums[mid-1]) || (mid == begin && nums[mid] <= nums[end]))     
	    return mid; 
    // General case 
    if (nums[mid] > nums[end]) 
    {
        begin = mid+1; 
        return binarySearchFindK(nums, begin, end); 
    }
    else
    {
        end = mid -1; 
        return binarySearchFindK(nums, begin, end); 
    }	
}  
int getPivot(vector<int>& nums)
{
    if( nums.size() == 0) return -1; 
    int result = binarySearchFindK(nums, 0, nums.size()-1); 
    return result; 
}

// Once you execute the above, you will know the shift k, 
// you can easily search for the element you need implementing the bottom 

int binarySearchSearch(vector<int>& nums, int begin, int end, int target, int pivot)
{
    if (begin > end) return -1; 
    int mid = (begin+end)/2;
    int n = nums.size();  
    if (n <= 0) return -1; 

    while(begin <= end)
    {
        mid = (begin+end)/2; 
        int midFix = (mid+pivot) % n; 
        if(nums[midFix] == target) 
        {
            return midFix; 
        }
        else if (nums[midFix] < target)
        {
            begin = mid+1; 
        }
        else
        {
            end = mid - 1; 
        }
    }
    return -1; 
}
int search(vector<int>& nums, int target) {
    int pivot = getPivot(nums); 
    int begin = 0; 
    int end = nums.size() - 1; 
    int result = binarySearchSearch(nums, begin, end, target, pivot); 
    return result; 
}

Hope this helps!=)
Soon Chee Loong,
University of Toronto

Solution 12 - C++

public class PivotedArray {

//56784321 first increasing than decreasing
public static void main(String[] args) {
	// TODO Auto-generated method stub
	int [] data ={5,6,7,8,4,3,2,1,0,-1,-2};
	
	System.out.println(findNumber(data, 0, data.length-1,-2));

}

static int findNumber(int data[], int start, int end,int numberToFind){
	
	if(data[start] == numberToFind){
		return start;
	}
	
	if(data[end] == numberToFind){
		return end;
	}
	int mid = (start+end)/2;
	if(data[mid] == numberToFind){
		return mid;
	}
	int idx = -1;
	int midData = data[mid];
	if(numberToFind < midData){
		if(midData > data[mid+1]){
			idx=findNumber(data, mid+1, end, numberToFind);
		}else{
			idx =  findNumber(data, start, mid-1, numberToFind);
		}
	}
	
	if(numberToFind > midData){
		if(midData > data[mid+1]){
			idx =  findNumber(data, start, mid-1, numberToFind);
			
		}else{
			idx=findNumber(data, mid+1, end, numberToFind);
		}
	}
	return idx;
}

}

Solution 13 - C++

For a rotated array with duplicates, if one needs to find the first occurrence of an element, one can use the procedure below (Java code):

public int mBinarySearch(int[] array, int low, int high, int key)
{
	if (low > high)
		return -1; //key not present
	
	int mid = (low + high)/2;
	
	if (array[mid] == key)
		if (mid > 0 && array[mid-1] != key)
			return mid;
	
	if (array[low] <= array[mid]) //left half is sorted
	{
		if (array[low] <= key && array[mid] >= key)
			return mBinarySearch(array, low, mid-1, key);
		else //search right half
			return mBinarySearch(array, mid+1, high, key);
	}
	else //right half is sorted
	{
		if (array[mid] <= key && array[high] >= key)
			return mBinarySearch(array, mid+1, high, key);
		else
			return mBinarySearch(array, low, mid-1, key);
	}		
	
}

This is an improvement to codaddict's procedure above. Notice the additional if condition as below:

if (mid > 0 && array[mid-1] != key)

Solution 14 - C++

Here is a simple (time,space)efficient non-recursive O(log n) python solution that doesn't modify the original array. Chops down the rotated array in half until I only have two indices to check and returns the correct answer if one index matches.

def findInRotatedArray(array, num):

lo,hi = 0, len(array)-1
ix = None


while True:

	
	if hi - lo <= 1:#Im down to two indices to check by now
		if (array[hi] == num):	ix = hi
		elif (array[lo] == num): ix = lo
		else: ix = None
		break

	mid = lo + (hi - lo)/2
	print lo, mid, hi

    #If top half is sorted and number is in between
	if array[hi] >= array[mid] and num >= array[mid] and num <= array[hi]:
		lo = mid

    #If bottom half is sorted and number is in between
	elif array[mid] >= array[lo] and num >= array[lo] and num <= array[mid]:
		hi = mid


    #If top half is rotated I know I need to keep cutting the array down
	elif array[hi] <= array[mid]:
		lo = mid

    #If bottom half is rotated I know I need to keep cutting down
	elif array[mid] <= array[lo]:
		hi = mid

print "Index", ix

Solution 15 - C++

Try this solution

bool search(int *a, int length, int key)
{
int pivot( length / 2 ), lewy(0), prawy(length);
if (key > a[length - 1] || key < a[0]) return false;
while (lewy <= prawy){
	if (key == a[pivot]) return true;
	if (key > a[pivot]){
		lewy = pivot;
		pivot += (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}
	else{
		prawy = pivot;
		pivot -= (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}}
return false;
}

Solution 16 - C++

This code in C++ should work for all cases, Although It works with duplicates, please let me know if there's bug in this code.

#include "bits/stdc++.h"
using namespace std;
int searchOnRotated(vector<int> &arr, int low, int high, int k) {

	if(low > high)
		return -1;

	if(arr[low] <= arr[high]) {

		int p = lower_bound(arr.begin()+low, arr.begin()+high, k) - arr.begin();
		if(p == (low-high)+1)
			return -1;
		else
			return p; 
	}

	int mid = (low+high)/2;

	if(arr[low] <= arr[mid]) {

		if(k <= arr[mid] && k >= arr[low])
			return searchOnRotated(arr, low, mid, k);
		else
			return searchOnRotated(arr, mid+1, high, k);
	}
	else {

		if(k <= arr[high] && k >= arr[mid+1])
			return searchOnRotated(arr, mid+1, high, k);
		else
			return searchOnRotated(arr, low, mid, k);
	}
}
int main() {

	int n, k; cin >> n >> k;
	vector<int> arr(n);
	for(int i=0; i<n; i++) cin >> arr[i];
	int p = searchOnRotated(arr, 0, n-1, k);
	cout<<p<<"\n";
	return 0;
}

Solution 17 - C++

In Javascript

var search = function(nums, target,low,high) {
    low= (low || low === 0) ? low : 0;
    
    high= (high || high == 0) ? high : nums.length -1;
    
    if(low > high)
        return -1;
    
    let mid = Math.ceil((low + high) / 2);

    
    if(nums[mid] == target)
        return mid;
    
    if(nums[low] < nums[mid]) {
        // if key is in the left half
        if (nums[low] <= target && target <= nums[mid]) 
            // search the left half
            return search(nums,target,low,mid-1);
        else
            // search the right half                 
            return search(nums,target,mid+1,high);
    } else {
        // if the key is in the right half.
        if(nums[mid] <= target && nums[high] >= target) 
            return search(nums,target,mid+1,high)
        else
            return search(nums,target,low,mid-1)
    }
};

Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4

Solution 18 - C++

import java.util.*;

class Main{
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int arr[]=new int[n];
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        int min_index=0,max_index=n;

        for(int i=0;i<n;i++){
            arr[i]=sc.nextInt();
            if(arr[i]>max){
                max=arr[i];
            max_index=i;
            }
            if(arr[i]<min){
                min=arr[i];
                min_index=i;
            }
            
        }

        int element=sc.nextInt();
        int index;
        if(element>arr[n-1]){
            index=Arrays.binarySearch(arr,0,max_index+1,element);
        }
        else {
             index=Arrays.binarySearch(arr,min_index,n,element);
        }
        if(index>=0){
            System.out.println(index);
        }
        else{
            System.out.println(-1);
        }
    }
    
}

Solution 19 - C++

Here are my two cents:

  • If the array does not contain duplicates, one can find the solution in O(log(n)). As many people have shown it the case, a tweaked version of binary search can be used to find the target element.

  • However, if the array contains duplicates, I think there is no way to find the target element in O(log(n)). Here is an example shows why I think O(log(n)) is not possible. Consider the two arrays below:

a = [2,.....................2...........3,6,2......2]
b = [2.........3,6,2........2......................2]

All the dots are filled with the number 2. You can see that both arrays are sorted and rotated. If one wants to consider binary search, then they have to cut the search domain by half every iteration -- this is how we get O(log(n)). Let us assume we are searching for the number 3. In the frist case, we can see it hiding in the right side of the array, and on the second case it is hiding in the second side of the array. Here is what we know about the array at this stage:

  • left = 0
  • right = length - 1;
  • mid = left + (right - left) / 2;
  • arr[mid] = 2;
  • arr[left] = 2;
  • arr[right] = 2;
  • target = 3;

This is all the information we have. We can clearly see it is not enough to make a decision to exclude one half of the array. As a result of that, the only way is to do linear search. I am not saying we can't optimize that O(n) time, all I am saying is that we can't do O(log(n)).

Solution 20 - C++

There is something i don't like about binary search because of mid, mid-1 etc that's why i always use binary stride/jump search

How to use it on a rotated array? use twice(once find shift and then use a .at() to find the shifted index -> original index)

Or compare the first element, if it is less than first element, it has to be near the end

do a backwards jump search from end, stop if any pivot tyoe leement is found

if it is > start element just do a normal jump search :)

Solution 21 - C++

Implemented using C#

public class Solution {
        public int Search(int[] nums, int target) {
             if (nums.Length == 0) return -1;
                int low = 0;
                int high = nums.Length - 1;
                while (low <= high)
                {
                    int mid = (low + high) / 2;
                    if (nums[mid] == target) return mid;
                    if (nums[low] <= nums[mid]) // 3 4 5 6 0 1 2
                    {
                        if (target >= nums[low] && target <= nums[mid])
                            high = mid;
                        else
                            low = mid + 1;
                    }
                    else // 5 6 0 1 2 3 4
                    {
                        if (target >= nums[mid] && target <= nums[high])
                            low= mid;
                        else
                            high = mid - 1;
                    }
                }
                return -1;
        }
    }

Solution 22 - C++

Another approach that would work with repeated values is to find the rotation and then do a regular binary search applying the rotation whenever we access the array.

test = [3, 4, 5, 1, 2]
test1 = [2, 3, 2, 2, 2]

def find_rotated(col, num):
    pivot = find_pivot(col)
    return bin_search(col, 0, len(col), pivot, num)

def find_pivot(col):
    prev = col[-1]
    for n, curr in enumerate(col):
        if prev > curr:
            return n
        prev = curr
    raise Exception("Col does not seem like rotated array")

def rotate_index(col, pivot, position):
    return (pivot + position) % len(col)

def bin_search(col, low, high, pivot, num):
    if low > high:
        return None
    mid = (low + high) / 2
    rotated_mid = rotate_index(col, pivot, mid)
    val = col[rotated_mid]
    if (val == num):
        return rotated_mid
    elif (num > val):
        return bin_search(col, mid + 1, high, pivot, num)
    else:
        return bin_search(col, low, mid - 1,  pivot, num)

print(find_rotated(test, 2))
print(find_rotated(test, 4))
print(find_rotated(test1, 3))

Solution 23 - C++

My simple code :-

public int search(int[] nums, int target) {
    int l = 0;
    int r = nums.length-1;
    while(l<=r){
        int mid = (l+r)>>1;
        if(nums[mid]==target){
            return mid;
        }
        if(nums[mid]> nums[r]){
            if(target > nums[mid] || nums[r]>= target)l = mid+1;
            else r = mid-1;
        }
        else{
            if(target <= nums[r] && target > nums[mid]) l = mid+1;
            else r = mid -1;
        }
    }
    return -1;
}

Time Complexity O(log(N)).

Solution 24 - C++

Question: Search in Rotated Sorted Array

public class SearchingInARotatedSortedARRAY {
	public static void main(String[] args) {
		int[] a = { 4, 5, 6, 0, 1, 2, 3 };

		System.out.println(search1(a, 6));

	}

	private static int search1(int[] a, int target) {
		int start = 0;
		int last = a.length - 1;
		while (start + 1 < last) {
			int mid = start + (last - start) / 2;

			if (a[mid] == target)
				return mid;
			// if(a[start] < a[mid]) => Then this part of the array is not rotated
			if (a[start] < a[mid]) {
				if (a[start] <= target && target <= a[mid]) {
					last = mid;
				} else {
					start = mid;
				}
			}
			// this part of the array is rotated
			else {
				if (a[mid] <= target && target <= a[last]) {
					start = mid;
				} else {
					last = mid;
				}
			}
		} // while
		if (a[start] == target) {
			return start;
		}
		if (a[last] == target) {
			return last;
		}
		return -1;
	}
}

Solution 25 - C++

Swift Solution 100% working tested

 func searchInArray(A:[Int],key:Int)->Int{
        for i in 0..<A.count{
            if key == A[i] {
                print(i)
                return i
            }
        }
        print(-1)
        return -1
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonesView Question on Stackoverflow
Solution 1 - C++codaddictView Answer on Stackoverflow
Solution 2 - C++ChuanRocksView Answer on Stackoverflow
Solution 3 - C++MaxView Answer on Stackoverflow
Solution 4 - C++RomanKView Answer on Stackoverflow
Solution 5 - C++Akki JavedView Answer on Stackoverflow
Solution 6 - C++Sebastian Paaske TørholmView Answer on Stackoverflow
Solution 7 - C++NIKUNJ BHARTIAView Answer on Stackoverflow
Solution 8 - C++Henk HoltermanView Answer on Stackoverflow
Solution 9 - C++SivGoView Answer on Stackoverflow
Solution 10 - C++Srikant AggarwalView Answer on Stackoverflow
Solution 11 - C++Chee Loong SoonView Answer on Stackoverflow
Solution 12 - C++RockSolidView Answer on Stackoverflow
Solution 13 - C++ranjeeth1978View Answer on Stackoverflow
Solution 14 - C++LeonView Answer on Stackoverflow
Solution 15 - C++BartView Answer on Stackoverflow
Solution 16 - C++SiddhantView Answer on Stackoverflow
Solution 17 - C++DollyView Answer on Stackoverflow
Solution 18 - C++saloniView Answer on Stackoverflow
Solution 19 - C++KhalfellaView Answer on Stackoverflow
Solution 20 - C++yozaamView Answer on Stackoverflow
Solution 21 - C++Jaydeep ShilView Answer on Stackoverflow
Solution 22 - C++barracelView Answer on Stackoverflow
Solution 23 - C++HeadAndTailView Answer on Stackoverflow
Solution 24 - C++Soudipta DuttaView Answer on Stackoverflow
Solution 25 - C++Mr.Javed MultaniView Answer on Stackoverflow