给定旋转的排序数组,如何找到该数组中的最大值?

时间:2015-01-13 23:41:50

标签: java algorithm divide-and-conquer

我已经考虑了很多,并且无法找到最佳解决方案。我正准备进行技术面试,但我找不到与这个问题有关的东西。我的第一步是实现一个天真的O(n)算法,该算法搜索整个数组以找到最大整数。现在我知道我可以做得比这更好,所以我想也许有办法使用二进制搜索或利用至少一半的数组完全排序的事实。也许您可以找到中间值并将其与数组的开头和结尾进行比较。

示例:

[5,7,11,1,3]将返回11。

[7,9,15,1,3]将返回15.

6 个答案:

答案 0 :(得分:1)

您必须以巧妙的方式进行二进制搜索才能实现O(lg n)绑定。观察到max元素右侧的元素是min(如果数组根本没有旋转,则为none)。所以进行常规二进制搜索,但检查索引mid的元素是否为max,如果不比较每个左/右子数组中的第一个和最后一个元素。如果左侧子阵列中的first<last,您知道左侧子阵列已排序并向右移动,否则您将向左移动。

假设数组被称为a,它有n个元素。

/* check if not rotated at all */
int ans = INFINITY;
if(a[0] < a[n-1] || n == 1)
{   ans = a[n-1];
    return;
}

/* array is certainly rotated */
int l = 0, r = n-1;
while(r - l > 5)
{   int m = (l + r) / 2;
    if(a[m] > a[m+1]) { ans = a[m]; break; }
    else
    {   if(a[l] < a[m-1]) l = m+1;
        else r = m-1;
    }
}

/* check the remaining elements (at most 5) in a loop */
if(ans == INFINITY)
{   for(int i = l; i <= r; i++)
    {   ans = max(ans, a[i]);
    }
}

我没有测试过这段代码。当元素数量为5或更少时,我打破的原因是要确保任一子阵列中的元素数量至少为2(因此您可以确定第一个和最后一个元素不是同一个元素)。如果有什么需要解决的话,你必须自己尝试并修复它。希望这会有所帮助。

答案 1 :(得分:1)

在排序后的数组(甚至旋转)中,可以确保使用二进制搜索(O(log2(n))。


/**
* Time complexity: O(log2(n))
* Space complexity: O(1)
*
* @param nums
* @return
*/
public int findMax(int[] nums) {
        // binary search
        int left = 0;
        int right = nums.length - 1;

        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[left] < nums[mid]) {
                left = mid;
            } else if (nums[left] > nums[mid]) {
                right = mid - 1;
            } else {    
                // subtility here if there are duplicate elements in the array.
                // shift the left linearly
                left = left + 1;
            }
        }
        return nums[left];
    }

答案 2 :(得分:0)

使用修改的二进制搜索来消除一半排序的子阵列(如果有两个排序的子阵列在每个步骤中删除“较低”的子阵列),同时跟踪可能更新的最大值。

#include <iostream>
#include <cstdlib>
#include <vector>

int main(int argc, char** argv)
{   
    std::vector<int> nums;
    for(int i = 1; i < argc; i++)
        nums.push_back(atoi(argv[i]));

    int start = 0;
    int end = argc - 2;
    int max = nums[start];
    while(start <= end) {
        int mid = (start + end) >> 1;
        int cand;
        if(nums[start] <= nums[mid])  {
            start = mid + 1;
        } else {
            end = mid - 1;
        }
        cand = nums[mid];
        if(cand > max)
           max = cand;
    }
    std::cout << max << std::endl;
    return 0;
}

答案 3 :(得分:0)

问题:在旋转的排序数组中找到最大值。数组没有任何重复: 解决方案:使用二进制搜索。 想法:永远记住在Sorted Rotated Array中,最大的元素将始终位于数组的左侧。同样,最小的元素将始终位于数组的右侧。 代码是:

public class Test19 {

    public static void main(String[] args) {
        int[] a = { 5, 6, 1, 2, 3, 4 };
        System.out.println(findLargestElement(a));

    }

    private static int findLargestElement(int[] a) {

        int start = 0;
        int last = a.length - 1;

        while (start + 1 < last) {

            int mid = (last - start) / 2 + start;
            if (mid < start) {
                mid = start;
            }
            if (mid > start) {
                last = mid - 1;
            } else {
                mid--;
            }

        } // while

        if (a[start] > a[last]) {
            return a[start];
        } else
            return a[last];

    }

}

答案 4 :(得分:0)

我想出的解决方案既紧凑又高效。 它基本上是二进制搜索算法的衍生产品。

PdfFormXObject

答案 5 :(得分:0)

这个问题用另一个版本的二分搜索很容易:


    int solve(vector<int>& a) {
        int n = a.size();
        int k=0;
        for(int b=n/2; b>=1; b/=2)
        {
            while(k+b<n && a[k+b] >= a[0])
                k += b;
        }
        return a[k];
    }