首页 文章

在循环排序数组中搜索元素

提问于
浏览
27

我们希望在复杂度不大于 O(log n) 的循环排序数组中搜索给定元素 .
示例:在 {5,9,13,1,3} 中搜索 13 .

我的想法是将循环数组转换为常规排序数组,然后对结果数组进行二进制搜索,但我的问题是我提出的算法是愚蠢的,在最坏的情况下需要 O(n)

for(i = 1; i < a.length; i++){
    if (a[i] < a[i-1]){
        minIndex = i; break;
    }
}

那么第i个元素的相应索引将由以下关系确定:

(i + minInex - 1) % a.length

很明显,我的转换(从循环到常规)算法可能需要O(n),所以我们需要一个更好的 .

根据ire_and_curses的想法,这是Java中的解决方案:

public int circularArraySearch(int[] a, int low, int high, int x){
    //instead of using the division op. (which surprisingly fails on big numbers)
    //we will use the unsigned right shift to get the average
    int mid = (low + high) >>> 1;
    if(a[mid] == x){
        return mid;
    }
    //a variable to indicate which half is sorted
    //1 for left, 2 for right
    int sortedHalf = 0;
    if(a[low] <= a[mid]){
        //the left half is sorted
        sortedHalf = 1;
        if(x <= a[mid] && x >= a[low]){
            //the element is in this half
            return binarySearch(a, low, mid, x);
        }
    }
    if(a[mid] <= a[high]){
        //the right half is sorted
        sortedHalf = 2;
        if(x >= a[mid] && x<= a[high] ){
            return binarySearch(a, mid, high, x);
        }
    }
    // repeat the process on the unsorted half
    if(sortedHalf == 1){
        //left is sorted, repeat the process on the right one
        return circularArraySearch(a, mid, high, x);
    }else{
        //right is sorted, repeat the process on the left
        return circularArraySearch(a, low, mid, x);
    }
}

希望这会奏效 .

16 回答

  • 49

    这是一个适用于Java的示例 . 由于这是一个排序数组,您可以利用它并运行二进制搜索,但需要稍微修改它以满足数据透视表的位置 .

    该方法如下所示:

    private static int circularBinSearch ( int key, int low, int high )
    {
        if (low > high)
        {
            return -1; // not found
        }
    
        int mid = (low + high) / 2;
        steps++;
    
        if (A[mid] == key)
        {
            return mid;
        }
        else if (key < A[mid])
        {
            return ((A[low] <= A[mid]) && (A[low] > key)) ?
                   circularBinSearch(key, mid + 1, high) :
                   circularBinSearch(key, low, mid - 1);
        }
        else // key > A[mid]
        {
            return ((A[mid] <= A[high]) && (key > A[high])) ?
                   circularBinSearch(key, low, mid - 1) :
                   circularBinSearch(key, mid + 1, high);
        }
    }
    

    现在为了减轻烦恼,这里有一个很好的小类来验证算法:

    public class CircularSortedArray
    {
        public static final int[] A = {23, 27, 29, 31, 37, 43, 49, 56, 64, 78, 
                                       91, 99, 1, 4, 11, 14, 15, 17, 19};
        static int steps;
    
        // ---- Private methods ------------------------------------------
    
        private static int circularBinSearch ( int key, int low, int high )
        {
            ... copy from above ...
        }
    
        private static void find ( int key )
        {
            steps = 0;
            int index = circularBinSearch(key, 0, A.length-1);
            System.out.printf("key %4d found at index %2d in %d steps\n",
                              key, index, steps);
        }
    
        // ---- Static main -----------------------------------------------
    
        public static void main ( String[] args )
        {
            System.out.println("A = " + Arrays.toString(A));
            find(44);   // should not be found
            find(230);
            find(-123);
    
            for (int key: A)  // should be found at pos 0..18
            {
                find(key);
            }
        }
    }
    

    那给你一个输出:

    A = [23, 27, 29, 31, 37, 43, 49, 56, 64, 78, 91, 99, 1, 4, 11, 14, 15, 17, 19]
    key   44 found at index -1 in 4 steps
    key  230 found at index -1 in 4 steps
    key -123 found at index -1 in 5 steps
    key   23 found at index  0 in 4 steps
    key   27 found at index  1 in 3 steps
    key   29 found at index  2 in 4 steps
    key   31 found at index  3 in 5 steps
    key   37 found at index  4 in 2 steps
    key   43 found at index  5 in 4 steps
    key   49 found at index  6 in 3 steps
    key   56 found at index  7 in 4 steps
    key   64 found at index  8 in 5 steps
    key   78 found at index  9 in 1 steps
    key   91 found at index 10 in 4 steps
    key   99 found at index 11 in 3 steps
    key    1 found at index 12 in 4 steps
    key    4 found at index 13 in 5 steps
    key   11 found at index 14 in 2 steps
    key   14 found at index 15 in 4 steps
    key   15 found at index 16 in 3 steps
    key   17 found at index 17 in 4 steps
    key   19 found at index 18 in 5 steps
    
  • 5

    您只需使用简单的二进制搜索,就好像它是一个常规排序数组 . 唯一的技巧是你需要旋转数组索引:

    (index + start-index) mod array-size
    

    其中start-index是圆形数组中第一个元素的偏移量 .

  • 0
    public static int _search(int[] buff, int query){
        int s = 0;
        int e = buff.length;
        int m = 0; 
    
        while(e-s>1){
            m = (s+e)/2;
            if(buff[offset(m)] == query){
                return offset(m);
            } else if(query < buff[offset(m)]){
                e = m;
            } else{
                s = m;
            }
        }
    
        if(buff[offset(end)]==query) return end;
        if(buff[offset(start)]==query) return start;
        return -1;
    }
    
    public static int offset(int j){
        return (dip+j) % N;
    }
    
  • 0

    guirgis:发布一个面试问题很蹩脚,猜猜你没得到这份工作:-(

    使用特殊的cmp函数,您只需要一次定期二进制搜索 . 就像是:

    def rotatedcmp(x, y):
      if x and y < a[0]:
        return cmp(x, y)
      elif x and y >= a[0]:
        return cmp(x, y)
      elif x < a[0]:
        return x is greater
      else:
        return y is greater
    

    如果你可以依赖int下溢从每个元素中删除一个[0] - MIN_INT,并使用常规比较 .

  • 0

    您可以使用二进制搜索来查找最小元素的位置,并将其减少为O(Log n) .

    你可以找到位置(这只是一个算法草图,它是不准确的,但你可以从中得到想法):
    我< - 1
    2. j < - n
    3.而我<j
    3.1 . k < - (j-i)/ 2
    3.2 . 如果arr [k] <arr [i]则j < - k 3.3 . 否则我< - k

    找到最小元素的位置后,您可以将该数组视为两个已排序的数组 .

  • 0

    虽然批准的答案是最佳的,但我们也可以使用类似和更清晰的算法 .

    • 运行二进制搜索以查找pivot元素(旋转数组的位置) . O(logn)

    • 枢轴的左半部分将按降序排序,在此处运行向后二进制搜索以获得密钥 . O(logn)

    • 枢轴的右半部分将按递增顺序排序,在此半部分中为密钥运行前向二进制搜索 . O(logn)

    • 返回从步骤2和3找到的关键索引 .

    总时间复杂度: O(logn)

    欢迎思考 .

  • 0

    不是很优雅,但是我的头顶 - 只需使用二分搜索来找到旋转阵列的枢轴,然后再次执行二分搜索,补偿枢轴的偏移 . 有点愚蠢地执行两次完整搜索,但它确实满足条件,因为O(log n)O(log n)== O(log n) . 保持简单和愚蠢(tm)!

  • 9

    对于搜索的低,中,高索引处的值,您有三个值 lmh . 如果你认为你会继续寻找每种可能性:

    // normal binary search
    l < t < m    - search(t,l,m)
    m < t < h    - search(t,m,h)
    
    // search over a boundary
    l > m, t < m - search(t,l,m)
    l > m, t > l - search(t,l,m)
    m > h, t > m - search(t,m,h)  
    m > h, t < h - search(t,m,h)
    

    这是一个考虑目标值在哪里,并搜索一半空间的问题 . 最多一半的空间将包含在其中,并且很容易确定目标值是否在那一半或另一半 .

    这是一个元问题 - 你是否认为二元搜索它是如何经常呈现的 - 在两点之间找到一个值,或者更一般地说是一个抽象搜索空间的重复划分 .

  • 0

    我想你可以使用这段代码找到偏移量:

    public static int findOffset(int [] arr){
            return findOffset(arr,0,arr.length-1);
        }
    private static int findOffset(int[] arr, int start, int end) {
    
        if(arr[start]<arr[end]){
            return -1;
        }
        if(end-start==1){
            return end;
        }
        int mid = start + ((end-start)/2);
        if(arr[mid]<arr[start]){
            return findOffset(arr,start,mid);
        }else return findOffset(arr,mid,end);
    }
    
  • 0

    简单的二进制搜索,稍有改动 .

    旋转阵列索引=(i pivot)%size

    pivot是索引i 1,其中a [i]> a [i 1] .

    #include <stdio.h>
    #define size 5
    #define k 3
    #define value 13
    int binary_search(int l,int h,int arr[]){
    
    int mid=(l+h)/2;
    
    if(arr[(mid+k)%size]==value)
        return (mid+k)%size;
    
    if(arr[(mid+k)%size]<value)
        binary_search(mid+1,h,arr);
    else
        binary_search(l,mid,arr);
    }
    
    int main() {
        int arr[]={5,9,13,1,3};
        printf("found at: %d\n", binary_search(0,4,arr));
        return 0;
    }
    
  • -1

    这是一个与二进制搜索相关的想法 . 只需继续为正确的数组索引绑定索引,左索引绑定将存储在步长中:

    step = n
    pos = n
    while( step > 0 ):
        test_idx = pos - step   #back up your current position
        if arr[test_idx-1] < arr[pos-1]:
            pos = test_idx
        if (pos == 1) break
        step /= 2 #floor integer division
    return arr[pos]
    

    为了避免(pos == 1)的事情,我们可以循环备份(进入负数)并取(pos-1)mod n .

  • 0

    下面是使用二进制搜索的C实现 .

    int rotated_sorted_array_search(int arr[], int low, int high, int target)
    {
        while(low<=high)
        {
            int mid = (low+high)/2;
    
            if(target == arr[mid])
                return mid;
    
            if(arr[low] <= arr[mid])
            {
                if(arr[low]<=target && target < arr[mid])
                {
                    high = mid-1;
                }
                else
                    low = mid+1;
            }
            else
            {
                if(arr[mid]< target && target <=arr[high])
                {
                    low = mid+1;
                }
                else
                    high = mid-1;
            }
        }
        return -1;
    }
    
  • 0

    您可以通过利用数组排序的事实来实现此目的,除了数据透视值及其邻居之一的特殊情况 .

    • 找到数组a的中间值 .

    • 如果 a[0] < a[mid] ,则对数组前半部分中的所有值进行排序 .

    • 如果 a[mid] < a[last] ,则对数组后半部分中的所有值进行排序 .

    • 取出已排序的一半,并检查您的值是否在其中(与该半部分中的最大idx相比) .

    • 如果是这样,只需二分之一搜索 .

    • 如果没有,则必须是未分类的一半 . 拿这一半重复这个过程,确定那一半的哪一半被分类,等等

  • 0

    这是javascript中的解决方案 . 用几个不同的数组测试它似乎工作 . 它基本上使用ire_and_curses描述的相同方法:

    function search(array, query, left, right) {
      if (left > right) {
        return -1;
      }
    
      var midpoint = Math.floor((left + right) / 2);
      var val = array[midpoint];
      if(val == query) {
        return midpoint;
      }
    
      // Look in left half if it is sorted and value is in that 
      // range, or if right side is sorted and it isn't in that range.
      if((array[left] < array[midpoint] && query >= array[left] && query <= array[midpoint])
        || (array[midpoint] < array[right] 
            && !(query >= array[midpoint] && query <= array[right]))) {
        return search(array, query, left, midpoint - 1);
      } else {
        return search(array, query, midpoint + 1, right);
      }
    }
    
  • 7

    Ruby 中的一个简单方法

    def CircularArraySearch(a, x)
      low = 0
      high = (a.size) -1
    
      while low <= high
        mid = (low+high)/2
        if a[mid] == x
          return mid
        end
        if a[mid] <= a[high]
          if (x > a[mid]) && (x <= a[high])
            low = mid + 1
          elsif high = mid -1
          end
        else
          if (a[low] <= x) && (x < a[mid])
            high = mid -1
          else
            low = mid +1
          end
        end
      end
      return -1
    end
    
    a = [12, 14, 18, 2, 3, 6, 8, 9]
    x = gets.to_i
    p CircularArraySearch(a, x)
    
  • 0

    检查这个系数,

    def findkey():
        key = 3
    
        A=[10,11,12,13,14,1,2,3]
        l=0
        h=len(A)-1
        while True:
            mid = l + (h-l)/2
            if A[mid] == key:
                return mid
            if A[l] == key:
                return l
            if A[h] == key:
                return h
            if A[l] < A[mid]:
                if key < A[mid] and key > A[l]:
                    h = mid - 1
                else:
                    l = mid + 1
            elif A[mid] < A[h]:
                if key > A[mid] and key < A[h]:
                    l = mid + 1
                else:
                    h = mid - 1
    
    if __name__ == '__main__':
        print findkey()
    

相关问题