6

Suppose I have a sorted array [1, 2, 3, 4, 5, 6]. I can apply binary search to find any number but what modification in my binary search logic I have to do If my sorted array shifted left by some unknown number. Like [4, 5, 6, 1, 2, 3].

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
Sumeet Kumar Yadav
  • 11,912
  • 6
  • 43
  • 80

5 Answers5

7
  1. We can find the shift using binary search. We need to find the first number which is less than the first element of the given array. Something like this:

    def getShift():
        if a[n - 1] > a[0]:
             return 0 // there is no shift
        low = 0 // definitely not less than a[0]
        high = n - 1 // definitely less than a[0]
        while high - low > 1:
            mid = (low + high) / 2
            if a[mid] < a[0]:
                high = mid
            else
                low = mid
        return high
    
  2. Now know the shift so we can run standard binary search over two intervals: [0, shift) and [shift, n - 1].

The time complexity is O(log n)(because we run 3 binary searches).

kraskevich
  • 18,368
  • 4
  • 33
  • 45
  • 1
    Finally, a real solution to the problem that doesn't involve wasting the knowledge of having almost sorted list. +1 – amit Feb 11 '15 at 19:31
  • It is not necessary to locate where the shift is; you can do the normal binary search with a simple modification by comparing `a[mid]` and the searched for value `x` with `a[0]` at the same time. See my answer. – Matt Feb 12 '15 at 20:19
1

Just re-sort the array after the unknown shift. It will be computationally expensive, but it will be correct.

Additionally, you could also just do a linear sort at this point, since sorting and searching will take O(n*log(n)). Doing a linear search via brute force will only be O(n).

Lawrence Aiello
  • 4,560
  • 5
  • 21
  • 35
  • yes, since this would be more efficient and less error prone then trying to reinvent the wheel to say with a modified binary search – jgr208 Feb 11 '15 at 19:18
  • Sorry, but I really dislike this answer. "computationaly expansive" by exponential factor, this approach will be approximately as efficient as just doing a linear search - both are correct, and both takes exponential more time than binary search – amit Feb 11 '15 at 19:24
  • So then you dislike this answer even though it is correct? – Lawrence Aiello Feb 11 '15 at 19:25
  • I dislike the answer because I am pretty sure there should be exponentially better solution to it, yes. (Unless you argue you cannot modify binary search, and even then - linear scan is still more efficient). Correctness is not everything, efficiency is also an issue. Hack, why not just do a [monkey sort](http://en.wikipedia.org/wiki/Bogosort) on the array and then search? – amit Feb 11 '15 at 19:27
  • 2
    Well yes, there most likely is. But in a real world of deadlines and management, sometimes we don't have time to come up with elegant solutions. This is quick and it works. – Lawrence Aiello Feb 11 '15 at 19:27
  • @amit i argue you can not without wasting more memory of a computer, to modify a binary search also yes linear is efficient but imagine the list is large and the number is at the very end you want to find. – jgr208 Feb 11 '15 at 19:28
  • Linear scan is quicker both to implement and to run and it works too, so there is a simpler AND more efficient solution. – amit Feb 11 '15 at 19:28
  • @jgr208 So what? Linear scan will traverse the list once - each element is scanned exactly once. If you sort, you need to traverse each element O(logn) times (on average), and for this special modification of the array - you are much more likely to fall into the worst cases of algorithms such as quick sort (and make the run time quadric) – amit Feb 11 '15 at 19:30
  • @amit was the so what to the memory or sorting? – jgr208 Feb 11 '15 at 19:33
  • 2
    Actually yes....he is right. Sorting and searching will take O(n log(n)) where as linear search takes O(n). I will update the answer. – Lawrence Aiello Feb 11 '15 at 19:33
1

You only need to run through the regular binary search algorithm once, with a modification on the logic as to when to select the upper or lower search window. The modification is based on additional comparisons with the first element in the shifted array so that you know which segment of the array you are in. You can do this without having to actually find the precise location of the split.

In ruby:

LIST = [6,7,8,9,10,1,2,3,4,5]

def binary_search(x)
  first = LIST[0]
  low = 0
  high = LIST.size-1
  while low <= high
    mid = low + (high-low)/2 # avoid overflow
    return mid if x == LIST[mid]
    if (LIST[mid] < first) != (x < first) || LIST[mid] < x
      low = mid + 1
    else
      high = mid - 1
    end
  end
  return -1 # not found
end

1.upto(10) do |x|
  puts "#{x} found at index #{binary_search(x)}"
end

Output:

1 found at index 5
2 found at index 6
3 found at index 7
4 found at index 8
5 found at index 9
6 found at index 0
7 found at index 1
8 found at index 2
9 found at index 3
10 found at index 4
Matt
  • 20,108
  • 1
  • 57
  • 70
0

Method 1

Actually with an unknown shift you can still do a binary but its kinda wonky.

One method is doubling the size of the list i.e:

[4,5,6,   1,2,3, 4,5,6,   1,2,3]
# basically  mylist.extend(mylist)

As you can see I just doubled the size but the middle section is still ordered.

Now you can go through the list until

list[i-1] > list[i]

This will be you start of the binary search and the same amount at the end would be the end of your binary list.

start = i
end = len(list) -1

Now you can do the binary search

Depending of the data average could be lower then O(n)

Method 2

You can resort the list and do the the binary search:

O(nlog(n)) + log(n)

Method 3

Linear search of all elements

O(n)

Jay
  • 2,656
  • 1
  • 16
  • 24
  • If we go through the entire list to find `list[i - 1] > list[i]`, it is easier to use a simple linear search(the time complexity is the same in the worst case). – kraskevich Feb 11 '15 at 19:21
  • that is true, and this also uses more memory of the computer. so would not be good for an embedded application. – jgr208 Feb 11 '15 at 19:22
  • True about big-o but on average (depending on the data) it could be faster especially if the shift is small. – Jay Feb 11 '15 at 19:24
0

Just simple binary search without doubling or sorting or any other preprocessing of array

so lets start

l = 0; 
r = n-1;

and

m = (l + r)/2

if we are searching for value v than:

1) if (there is no jump between l and m)

array[l] < v < array[m] or 

(if there is jump between l and m)

v < array[m] < array[l] or  
array[m] < array[l] < v 

than v is between l and r, and we can make r = m

2) if v is equal to array[l] array[r] or array[m] we found it

3) in all others cases v is somewhere between m and r and we can make l = m

4) repeat for new l and r

Lurr
  • 861
  • 4
  • 11