在旋转的排序数组中搜索数字

时间:2009-12-10 05:19:08

标签: data-structures

给定一个可以旋转的分类数组,以最小的时间复杂度在其中找到一个元素。

例如:数组内容可以是[8,1,2,3,4,5]。假设您在其中搜索了8个。

21 个答案:

答案 0 :(得分:39)

解决方案仍然适用于二进制搜索,因为您需要将数组分成两部分进行检查。

在排序数组中,您只需查看每个部分并确定该元素是否位于第一部分(让我们称之为A)或第二部分(B)。因为,通过定义排序数组,分区A和B将被排序,这只需要对分区边界和搜索关键字进行一些简单的比较。

在旋转的有序数组中,只能保证对A和B中的一个进行排序。如果元素位于已排序的部分内,则解决方案很简单:只需执行搜索,就像进行常规二进制搜索一样。但是,如果必须搜索未排序的部分,则只需在未排序的部分上递归调用搜索功能。

最终会给出O(lg n)的时间复杂度。

(实际上,我认为这样的数据结构会附带一个索引来表示数组已经旋转了多少个位置。)

修改:Google上的搜索引导我在CodeGuru讨论相同问题的this有点过时(但正确)主题。为了添加我的答案,我将复制一些在那里给出的伪代码,以便它出现在我的解决方案中(思路遵循相同的路线):

Search(set):
    if size of set is 1 and set[0] == item 
        return info on set[0]
    divide the set into parts A and B
    if A is sorted and the item is in the A's range
        return Search(A)
    if B is sorted and the item is in the B's range
        return Search(B)
    if A is not sorted
        return Search(A)
    if B is not sorted
        return Search(B)
    return "not found"

答案 1 :(得分:18)

O(log(N))

减少到找到最大数字位置的问题,这可以通过检查区域的第一个和最后一个和中间数来完成,递归地减少区域,划分和征服,这是O(log(N))否大于二进制搜索O(log(N))。

编辑: 例如,你有

6 7 8 1 2 3 4 5  
^       ^     ^ 

通过查看3个数字,您知道最小/最大数字的位置(稍后将被称为标记)位于6 7 8 1 2区域内,因此3 4 5不在考虑范围内(通常由移动你的区域开始/结束索引(int)指向数字6和2)。

下一步,

6 7 8 1 2  
^   ^   ^  

再次,您将获得足够的信息来判断标记的哪一侧(左侧或右侧),然后该区域再次减少一半(至6 7 8)。

这个想法是:我认为你可以改进一个更好的版本,实际上,对于一次采访,一个干净的代码的OK算法比使用OK代码的最佳算法更好:你最好手工 - 在一些加热。

祝你好运!

答案 2 :(得分:8)

我最近在一次采访中被问到这个问题。问题是描述了一个搜索循环排序数组中“密钥”的算法。我也被要求编写相同的代码。 这就是我想出的:

使用分而治之二进制搜索。 对于每个子数组,检查数组是否已排序。如果排序使用经典二进制搜索 e.g

数据[开始]< data [end]表示数据已排序。用户二进制 其他 将数组进一步划分,直到我们得到排序数组。

    public boolean search(int start,int end){
    int mid =(start+end)/2;
    if(start>end)
    {
        return  false;
    }
    if(data[start]<data[end]){
        return this.normalBinarySearch(start, end);
    }
    else{
        //the other part is unsorted.
        return (this.search(start,mid) ||
        this.search(mid+1,end));
    }
}

其中normalBinarySearch是一个简单的二进制搜索。

答案 3 :(得分:5)

它是对普通二进制搜索的简单修改。实际上我们它将适用于旋转和排序的数组。在排序数组的情况下,它最终将完成比实际需要更多的工作。

对于旋转数组,当您将数组拆分为两个子数组时,其中一个子数组可能不会按顺序排列。您可以通过比较子阵列中的第一个和最后一个值来轻松检查是否每个半部分都已排序。

了解每个子阵列是否已排序,我们可以选择下一步做什么。

1)对左子阵列进行排序,并且该值在左子阵列的范围内(检查两端!)

然后搜索递归搜索左子阵列。

2)对右子阵列进行排序,并且该值在右子阵列的范围内(检查两端!)

然后递归搜索右子阵列。

3)左边未排序

然后递归搜索左子阵列

4)右边没有排序

然后递归搜索右子阵列。

注意:这与普通二进制搜索之间的区别在于我们不能简单地通过简单地比较左子阵列的最后一个值(右子阵列的第一个值)来做出我们的决定。该值必须严格地在左或右子阵列中,并且该子阵列必须排序,否则我们必须递归到未排序的子阵列。

以下是一些实现此目的的Objective-C:

@implementation BinarySearcher

- (BOOL)isValue:(int)value inArray:(int[])array withArraySize:(int)size {

    return [self subSearchArray:array forValue:value fromIndex:0 toIndex:size -1];
}

- (BOOL)subSearchArray:(int[])array forValue:(int)value fromIndex:(int)left toIndex:(int)right {

    if (left <= right) {

        int middle = (left + right) / 2;

        BOOL leftArraySorted = array[left] <= array[middle];
        BOOL rightArraySorted = array[middle + 1] <= array[right];

        if (array[middle] == value) {
            return YES;
        } else if (leftArraySorted && value >= array[left] && value < array[middle]) {
            return [self subSearchArray:array forValue:value fromIndex:left toIndex:middle];
        } else if (rightArraySorted && value >= array[middle + 1] && value <= array[right]) {
            return [self subSearchArray:array forValue:value fromIndex:middle + 1 toIndex:right];
        } else if (!leftArraySorted) {
            return [self subSearchArray:array forValue:value fromIndex:left toIndex:middle];
        } else if (!rightArraySorted) {
            return [self subSearchArray:array forValue:value fromIndex:middle + 1 toIndex:right];
        }
    }

    return NO;
}

@end

答案 4 :(得分:4)

在任何索引处,一个分区将被排序,其他分区将被排序。如果key位于已排序的分区中,则在有序数组中搜索,否则在非有序分区中进行搜索。

BS(lo, hi)
m = (lo + hi)/2
if(k = a[m])
    return m
b = 0
if(a[hi] > a[m])
    b = 1
if(b)
    if(k > a[m] && k<a[hi])
        BS(m+1, hi)
else
    BS(lo, m-1)

答案 5 :(得分:2)

以下是我的方法:

public static int findMin(int[] a, int start, int end){
  int mid = (start + end)/2;
  if(start == mid){
    return a[mid+1];
  }else if(a[start] > a[mid]){
    return findMin(a, start, mid);
  }else if(a[mid+1] > a[start]){
    return findMin(a, mid+1, end);
  }else{
    return a[mid+1];
  }
}

时间复杂度:O(log n)

答案 6 :(得分:2)

您可以使用仅公开

的类来包装数组

E get(int index);

并且表现为常规排序数组。 例如,如果您有4 5 1 2 3 4,则wrapper.get(0)将返回1.

现在您可以重复使用二进制搜索解决方案。

Wrapper看起来像这样:

class RotatedArrayWrapper<T> {
  int startIndex;
  private final List<T> rotatedArray;

  public RotatedArrayWrapper(List<T> rotatedArray) {
    this.rotatedArray = rotatedArray;
    //find index of the smalest element in array
    //keep in mind that there might be duplicates
    startIndex = ... 
  } 

  public T get(int index) {
    int size = rotatedArray.size();
    if (index > size) throw Exception...

    int actualIndex = (startIndex + index) % size;
    return rotatedArray.get(actualIndex);
  }
}

答案 7 :(得分:1)

Python实现。可以用来更加pythonic:

from bisect import bisect_left


def index(a, x):
    """Binary search to locate the leftmost value exactly equal to x.
    see http://docs.python.org/2/library/bisect.html#searching-sorted-lists

    >>> index([5, 14, 27, 40, 51, 70], 27)
    2
    >>> index([1, 2, 3, 4], 10)
    Traceback (most recent call last):
        ...
    ValueError
    """
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError


def _index_shifted(value, sequence, start, stop):
    """Recursive reset location and binary search"""
    # if at reset (min) and it's not the value, it's not there
    if start == stop and sequence[start] != value:
        return -1
    mid = (stop + start) // 2
    # check mid, since we are already here
    if sequence[mid] == value:
        return mid
    # right side is sorted
    elif sequence[mid] < sequence[stop]:
        # if value falls in range, search righ
        if sequence[stop] >= value > sequence[mid]:
            return index(sequence[mid:stop + 1], value) + mid
        # partition left side
        return _index_shifted(value, sequence, start, mid)
    # left side is sorted
    else:
        # if value falls in range, search left
        if sequence[mid] > value >= sequence[start]:
            return index(sequence[start:mid], value) + start
        # partition right side
        return _index_shifted(value, sequence, mid + 1, stop)


def index_shifted(sequence, value):
    """Returns index of value in a shifted sorted sequence; -1 if not present.

    >>> index_shifted([10, 13, 16, 19, 22, 25, 28, 31, 34, 37], 10)
    0
    >>> index_shifted([10, 13, 16, 19, 22, 25, 28, 31, 34, 37], 37)
    9
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 10)
    2
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 37)
    1
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 13)
    3
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 25)
    7
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], 10)
    5
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], -10)
    -1
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], 100)
    -1
    """
    return _index_shifted(value, sequence, 0, len(sequence) - 1)

答案 8 :(得分:0)

在最坏的情况下,您必须使用线性搜索并检查整个数组以查找目标,因为与集合不同,数组可能包含重复项。如果数组包含重复项,则无法在给定问题中使用二进制搜索。

如果对特定数组的查询不频繁,则可以使用标准二进制搜索,如果整个数组已排序(第一个元素严格小于最后一个元素),否则使用线性搜索。有关详细信息,请参阅有关StackOverflow的How fast can you make linear search?讨论和Thomas A. Limoncelli的10 Optimizations on Linear Search文章。

Hovewer,如果查询频繁,您可以在预处理步骤中按线性时间对输入数组进行排序(请参阅有关StackOverflow的Fastest algorithm for circle shift N sized array for M position讨论),并照常使用标准二进制搜索。

答案 9 :(得分:0)

我的解决方案: 效率:O(logn)

public static int SearchRotatedSortedArray(int l, int d, int[] array, int toFind) {
    if (l >= d) {
        return -1;
    }
    if (array[l] == toFind) {
        return l;
    }
    if (array[d] == toFind) {
        return d;
    }

    int mid = (l + d) / 2;
    if (array[mid] == toFind) {
        return mid;
    }

    if ((array[mid] > toFind && toFind > array[l]) || (array[mid] < toFind && array[d] < toFind)) {
        return SearchRotatedSortedArray(l, mid - 1, array, toFind);
    } else {
        return SearchRotatedSortedArray(mid + 1, d, array, toFind);
    }

}

答案 10 :(得分:0)

def搜索(数组,左,右,目标):         #停止递归条件。         如果不是数组:             返回-1         如果左==右:             如果array [left] == target else -1

,则返回左侧
    # Check if middle element is same as the target.
    mid = (left + right) / 2
    if array[mid] == target:
        return mid

    # Pivot point is at the right of the middle element.
    if array[left] <= array[mid]:
        if target >= array[left] and target <= array[mid]:
            return search(array, left, mid - 1, target)
        return search(array, mid + 1, right, target)

    # Pivot point is at the left of the middle element.
    if target >= array[mid] and target <= array[right]:
        return search(array, mid + 1, right, target)
    return search(array, left, mid - 1, target)

我找到了来自this post

的解决方案

答案 11 :(得分:0)

这是PYTHON中100%正常运行且经过测试的解决方案

编程以从已排序但已旋转的数组中查找数字

def findNumber(a, x, start, end):
  if start > end:
    return  -1
  mid = (start + end) / 2
  if a[mid] == x:
    return mid
  ## Case : 1  if a[start] to a[mid] is sorted , then search first half of array
  if a[start] < a[mid]:
    if (x >= a[start] and x <= a[mid]):
      return findNumber(a, x, start, mid - 1)
    else:
      return findNumber(a,x, mid + 1, end)
  ## Case: 2 if a[start] to a[mid] is not sorted , then a[mid] to a[end] mist be sorted
  else:
    if (x >= a[mid] and x <= a[end]):
      return findNumber(a, x, mid + 1, end)
    else:
      return findNumber(a,x, start, mid -1)


a = [4,5,6,7,0,1,2]
print "Your array is  : " , a
x = input("Enter any number to find in array : ")
result = findNumber(a, x, 0, len(a) - 1)
print "The element is present at %d position: ", result

答案 12 :(得分:0)

这是@Andrew Song的答案的C ++实现

int search(int A[], int s, int e, int k) {
    if (s <= e) {
        int m = s + (e - s)/2;
        if (A[m] == k)
            return m;
        if (A[m] < A[e] && k > A[m] && k <= A[e]) 
            return search(A, m+1, e, k);
        if (A[m] > A[s] && k < A[m] && k >= A[s]) 
            return search(A, s, m-1, k);
        if (A[m] < A[s]) 
            return search(A, s, m-1, k);
        if (A[m] > A[e])
            return search(A, m+1, e, k);
    }
    return -1;
}

答案 13 :(得分:0)

尝试这个解决方案,

     public static int findElement(int[] a, int key, int left, int right) {

           if (left > right) {
              return -1;
           }

           int mid = (left + right) / 2;

           if (key == a[mid]) {
              return mid;
           } else if (key < a[mid]) {
              return (a[left] <= a[mid] && a[left] < key ? findElement(
                a, key, left, mid - 1) : findElement(a, key,
                mid + 1, right));
           } else {
              return (a[mid] <= a[right] && a[right] < key ? findElement(
                a, key, left, mid - 1) : findElement(a, key,
                mid + 1, right));
    }

}

答案 14 :(得分:0)

这是一个O(logn)解决方案:经过测试。它适用于已排序和旋转的排序数组:

public static int rbs(int[] a, int l, int r, int t) {

    if (a[l] <= a[r]) {
        return bs(a, l, r, t);
    }

    if (r < l) {
        return -1;
    } else {
        int m = (l+r) / 2;
        if (a[m] == t) {
            return m;
        } else if (a[m] > t) { // check left half
            if (a[l] > a[m]) { // left is unsorted
                return rbs(a, l, m-1, t);
            } else { // left is sorted
                if (a[l] < t) { // t is in range
                    return bs(a, l, m-1, t);
                } else if (a[l] > t) { // t is out of range on left
                    if (a[r] >= t) {
                        return rbs (a, m+1, r, t);
                    } else
                        return -1;
                } else
                    return l;
            }
        } else { // other side
            if (a[r] < a[m]) { // right is unsorted
                return rbs(a, m+1, r, t);
            } else { // right is sorted
                if (a[r] > t) { // t is in range
                    return bs(a, m+1, r, t);
                } else if (a[r] < t) { // t is out of range on right side
                    if (a[l] <= t) {
                        return rbs (a, l, m-1, t);
                    } else
                        return -1;
                } else
                    return r;
            }
        }
    }
}


public static int bs(int[] a, int l, int r, int t) {

    int m = (l+r) / 2;

    if (r < l) {
        return -1;
    } else {
        if (a[m] == t)
            return m;
        else if (a[m] < t)
            return bs(a, m+1, r, t);
        else
            return bs (a, l, m-1, t);
    }
}

答案 15 :(得分:0)

首先找到数组中的Minimum元素然后将数组分成两部分。之后使用二进制搜索树搜索给定值。复杂性:O(logn) 如果必须在旋转的数组中找到Minimum元素,请使用相同的方法,只需跳过二进制搜索。复杂性:O(logn)

//Search an element in a sorted and pivoted array
    class SearchInPivotedSortedArray
    {
        //searchInOrtedPivotedArray : Return index of matched element with given value.
        public static int searchInSortedPivotedArray(int[] A, int value)
        {
            int min = findMinElement(A,0,A.Length-1);
            if (min == A[0])
               return binarySearchTree(A, 0, A.Length-1, value);


            if (value <= A[A.Length-1])
               return binarySearchTree(A, min, A.Length-1, value);
            else
               return binarySearchTree(A, 0, min-1, value);


        }
        //return index of Pivot element
        public static int findMinElement(int[] Array, int low, int high)
        {

            if (low >= high)
                return low;

            int mid = (low + high) / 2;
            if (mid > low && Array[mid] < Array[mid - 1])
                return mid;
            if (mid < high && Array[mid] > Array[mid + 1])
                return mid + 1;

            if (Array[mid] < Array[high])
                return findMinElement(Array, low, mid - 1);

            return findMinElement(Array, mid + 1, high);

        }
        //Return match element index, if not found return -1
        public static int binarySearchTree(int[] array, int low, int high,int value)
        {
            if (low > high)
                return -1;
            int mid = (low + high)/2;

            if (array[mid] == value)
                return mid;
            if (array[mid] > value)
                return binarySearchTree(array, low, mid - 1, value);
            else
                return binarySearchTree(array, mid + 1, high, value);

        }
    }

答案 16 :(得分:0)

public class RoatatedSorted {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub

    int[] rotArray = {5,6,7,8,9,10,15,16,17,1,2,3,4,};

    search(rotArray,0,rotArray.length-1,10);

}

private static void search(int[] a, int low, int high,int key) {

    //int mid =  low + (high-low)/2;
    //if(a[mid]==a[key]){System.out.println("element found at" + key+1 +" position"); return;}

    // find pos to split array
    int pos = findSplitIndex(a,low,high);
    System.out.println("split pos found at" + pos +" position");


    if(key>=a[low]&& key <=a[pos])
        bsearch(a,low,pos,key);
    if(key>=a[pos+1]&& key <=a[high])
        bsearch(a,pos+1,high,key);  
}


private static void bsearch(int[] a, int low, int high,int key) {
    // TODO Auto-generated method stub
    if(low>high) return;
    int mid =  low + (high-low)/2;
    if(a[mid]==key)
    {System.out.println("element found at" + ++mid +" position"); return;}
    if(a[mid] > key)
        bsearch(a,low,mid-1,key);
    if(a[mid]<key)
        bsearch(a,mid+1,high,key);
}

private static int findSplitIndex(int[] a, int low, int high) {
    // TODO Auto-generated method stub
    int mid;
    if(low>high)return -1;
    while(true) {
        mid =  low + (high-low)/2;
    if( a[mid]>a[mid+1])
    break;


    if(a[mid]>a[low])
        low=mid;
    if(a[high]>a[mid])
        high=mid;
}
    return mid;


}

}

答案 17 :(得分:0)

int findIndexInRotatedSort( vector<int> input, int s, int e, int toFind )
    {
        if (s > e || s >= input.size() || e < 0)
        {
            return -1;
        }

        int m = (s + e)/2;

        int sVal = input.at(s);
        int eVal = input.at(e);
        int mVal = input.at(m);

        if (sVal == toFind)
            return s;
        if (eVal == toFind)
            return e;
        if (mVal == toFind)
            return m;

        bool isFirstOrdered = (sVal < mVal);
        bool isSecondOrdered = (mVal < eVal);
        if (toFind > mVal)
        {
            if (!isSecondOrdered || toFind < eVal)
            {
                return findIndexInRotatedSort( input, m+1, e, toFind );
            }
            else
            {
                return findIndexInRotatedSort( input, s, m-1, toFind );
            }
        }
        else
        {
            if (!isFirstOrdered || toFind > sVal)
            {
                return findIndexInRotatedSort( input, s, m-1, toFind );
            }
            else
            {
                return findIndexInRotatedSort(  input, m+1, e, toFind );
            }
        }
    }

答案 18 :(得分:0)

//这个解决方案使用递归将数组划分为两个相等的子数组,并在单独的排序数组上应用二进制搜索,然后继续分割未排序的数组

public class SearchRotatedSortedArray
{
    public static void main(String... args)
    {
        int[] array={5,6,1,2,3,4};

        System.out.println(search(array,Integer.parseInt(args[0]),0,5));
    }

    public static boolean search(int[] array,int element,int start,int end)
    {   
    if(start>=end)
    {
        if (array[end]==element) return true;else return false;
    }

    int mid=(start+end)/2;
    if(array[start]<array[end])
    {
        return binarySearch(array,element,start,end);
    }

    return search(array,element,start,mid)||search(array,element,mid+1,end);    
}

    public static boolean binarySearch(int[] array,int element,int start,int end)
    {
        int mid;

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

            if(array[mid]==element)
            return true;

            if(array[mid]<element)
            {
                start=mid+1;

            }
            else
            {
                end=mid-1;

            }
        }

        return false;
    }
}

答案 19 :(得分:-1)

在旋转的有序数组中查找元素索引

Example : [6,7,8,1,2,3,5]

int findElementIndex(int []a, int element, int start, int end)
{
    int mid = (start + end)>>1;

    if(start>end)
        return -1;

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

    if(a[mid] < a[start])
    {
       if(element <= a[end] && element > a[mid])
        {
       return findElementIndex(a,element,mid+1,end);
        }
        else{
       return findElementIndex(a,element,start,mid-1);
        }
     }
    else  if(a[mid] > a[start]){
        if(element >= a[start] && element < a[mid])
             return findElementIndex(a,element,start,mid-1);
        else
            return findElementIndex(a,element,mid+1,end);
    }
    else if (a[mid] == a[start]){
        if(a[mid] != a[end]) // repeated elements
          return findElementIndex(a,element,mid+1,end);
        else
            int left = findElementIndex(a,element,start,mid-1);
            int right = findElementIndex(a,element,mid+1,end);

            return (left != -1) ? left : right;

    }

}

答案 20 :(得分:-1)

#include<iostream>
using namespace std;
int BinarySearch(int *a,int key, int length)
{
    int l=0,r=length-1,res=-1;
    while(l<=r)
    {
        int mid = (l+r)/2;
        if(a[mid] == key) {res = mid; break;}
                //Check the part of the array that maintains sort sequence and update index                   
                // accordingly.
        if((a[mid] < a[r] && ( key > a[mid] && key <=a[r]))
            || a[mid] > a[r] && !( key>=a[l] && key <a[mid]))
        {
                     l = mid+1;
        }
        else r = mid-1;
    }
    return res;
}
void main()
{
    int arr[10] = {6,7,8,9,10,13,15,18,2,3};
    int key = 8;
    cout<<"Binary Search Output: "<<BinarySearch(arr,key,10);
}