旋转有序数组搜索

时间:2016-04-26 07:08:07

标签: java algorithm sorting

处理下面的算法难题。发布问题陈述和解决方案。问题是,我们是否需要"搜索两半"部分是为了保证安全吗?或者当a[left] == a[mid]时,我们只能搜索正确的部分而不检查是否a[mid] == a[right] - 因为当a[left] == a[mid]时,我认为左边的所有元素都是相同的,并且无法满足搜索条件来查找值。

更详细地说,我的意思是,如果as,

是否可以安全地写下最后一个
else if (a[left] == a[mid]) {
            return search(a, mid + 1, right, x);
    }

问题陈述

给定已旋转未知次数的n个整数的排序数组,编写代码以查找元素 在数组中,您可以假设该数组最初按递增顺序排序

实施例, 输入在{15,16,19,20,25,1,3,4,5,7,10,14}中找到5 输出,8(数组中的索引为5)

代码

public static int search(int a[], int left, int right, int x) {
    int mid = (left + right) / 2;
    if (x == a[mid]) { // Found element
        return mid;
    }
    if (right < left) {
        return -1;
    }

    /* While there may be an inflection point due to the rotation, either the left or 
     * right half must be normally ordered.  We can look at the normally ordered half
     * to make a determination as to which half we should search. 
     */
    if (a[left] < a[mid]) { // Left is normally ordered.
        if (x >= a[left] && x < a[mid]) { 
            return search(a, left, mid - 1, x);
        } else {
            return search(a, mid + 1, right, x);
        }
    } else if (a[mid] < a[left]) { // Right is normally ordered.
        if (x > a[mid] && x <= a[right]) {
            return search(a, mid + 1, right, x);
        } else {
            return search(a, left, mid - 1, x);
        }               
    } else if (a[left] == a[mid]) { // Left is either all repeats OR loops around (with the right half being all dups)
        if (a[mid] != a[right]) { // If right half is different, search there
            return search(a, mid + 1, right, x);
        } else { // Else, we have to search both halves
            int result = search(a, left, mid - 1, x); 
            if (result == -1) {
                return search(a, mid + 1, right, x); 
            } else {
                return result;
            }
        }
    }
    return -1;
}

2 个答案:

答案 0 :(得分:2)

就您的问题而言,您的假设是完全正确的,您不必在那里搜索两半。您可以搜索右半部分,因为如果a[mid] != key,您的元素肯定在右半部分,如果它在数组中那么。

编辑:

上述结论不完整。我现在写一个新的,进一步考虑这个。

首先,如果在任何时间点,您正在搜索两半,那么二进制搜索没有意义,因为搜索的复杂性变为O(n)

现在,你就在那里,如果a[mid] == a[left],你的钥匙可以处于任何一半。但是,有一点你可以肯定的是,其中一半将使所有元素相等。

eg. Suppose a[12] = [1,1,1,2,2,2,2,2,2,2,2,2]
    rotate r=2 times, a`[12] = [2,2,1,1,1,2,2,2,2,2,2,2]
    so, a[mid] = a[left] = 2
    if key = 1, it is in left half.

    now, rotate r=9 times,
         a``[12] = [2,2,2,2,2,2,2,2,2,1,1,1]
    so, a[mid] = a[left] = 2
    if key = 1, it is in right half

因此,您需要搜索此数组的两半以获取密钥,这种情况实际上会使二进制搜索变得多余。

  

所以,现在,我甚至建议你使用我在下面提到的解决方案。

虽然如果没有限制,我想提出一个解决方案:

这是Binary Search算法的简单变体。

首先必须在数组中应用二进制搜索,并使用终止条件:

a[mid-1] > a[mid]

满足此条件后,您将获得索引k = mid,它为您提供了2个排序数组。

第一个数组A[0...k-1]和第二个数组A[k...n-1],假设n元素。

现在,请进行检查。

if(key > A[0])
    Binary Search in A[0...k-1]
else
    Binary Search in A[k...n-1]

一个例外,如果数组已轮换n次,您将无法获得k的值。二进制搜索整个数组以获取密钥。

EDIT2:通过评论回答您的问题

  

我想知道我的原始方法,如果[左]&lt; a [mid],x&gt; = a [left]&amp;&amp; x&lt; a [mid],如果只使用搜索(a,left,mid - 1,x)在左侧搜索是安全的,如果数组可以旋转n次?

如果a[left] < a[mid],那么我们可以确定a[left...mid]升序(排序)。因此,如果x >= a[left] && x < a[mid],只在左半边搜索就足够了。

  

如果您还可以澄清[left] == a [mid]和[mid] == a [right]的时间,我们需要搜索两个方向吗?

是。在这种情况下,我们需要搜索两个方向。比方说,例如。您的原始数组是{1,2,2,2,2,2,2,2,2,2},它会旋转一次。数组变为{2,1,2,2,2,2,2,2,2,2}。如果它旋转了8次,则变为{2,2,2,2,2,2,2,2,1,2}。现在,如果您的搜索关键字是1,则在开始时,在两种情况下都是a[mid] == a[left] == a[right],并且您的密钥位于不同的一半。因此,您需要搜索两半的密钥。

答案 1 :(得分:1)

您需要考虑两件事情。

首先,因为数组被旋转意味着它就像一个循环缓冲区,所以你需要以不同的方式访问数组以避免离开数组边界。

Normal

    array[index]

Circular

    array[index % size]

其次你需要考虑轮换,第一个有序项不是索引0,让我们说它是索引f作为第一个,所以要访问有序索引i,你使用{ {1}}。

i+f

要找到旋转的有序数组的第一项,我们可以检查排序条件的中断位置。

Normal

    array[index]

Circular

    array[index % size]

Rotated

    array[(index + first) % size]

现在你只需要在排序之前获得第一个索引..

// assuming ascending order a[0] <= a[1]

int find_first_index (int [] arr)
{
    for(int i = 0; i < arr.length; i++)
    {
        if(arr[i] > arr[(i+1) % arr.length])
        {
            return (i+1) % arr.length;
        }
    }

    return 0;
}

然后使用int f = find_first_index(arr); 而不是[(i+f) % arr.length]替换排序算法中的所有数组访问。所以你的代码就像这样......

arr[i]

.. The complete source-code is Here