【问题标题】:Given a rotated sorted array, how can I find the largest value in that array?给定一个旋转的排序数组,我怎样才能找到该数组中的最大值?
【发布时间】:2015-03-12 01:59:34
【问题描述】:

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

示例:

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

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

【问题讨论】:

  • 好的,继续你的思路。回答面试问题不是要知道答案,而是要解决问题。
  • 1.找到最小值。 2.返回左边的值。
  • 我认为最佳复杂度是 O(log(n))。读取第一个、中间和最后一个值。如果 first
  • 您的第一个数组似乎不符合条件。该数组应该是一个经过排序然后可能旋转的数组,对吗?在这种情况下,“排序”数组是[1, 5, 7, 6, 4, 10, 11],它没有排序——对吧?还是我误解了这个问题?如果我误解了,我认为您需要更好地解释数组的约束是什么。

标签: java algorithm divide-and-conquer


【解决方案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】:

您必须以一种巧妙的方式进行二分搜索才能实现 O(lg n) 界限。观察到最大元素右侧的元素是最小值(如果数组根本不旋转,则为无)。因此,请进行常规二进制搜索,但检查索引 mid 处的元素是否为最大值,如果不比较每个左/右子数组中的第一个和最后一个元素。如果first&lt;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(因此您可以确定第一个和最后一个不是同一个元素)。如果有什么要修复的,你必须自己尝试并修复它。希望这会有所帮助。

【讨论】:

  • 我认为这是正确的答案,但为什么要明确检查“未旋转”的情况?不是包含在 bsearch 部分吗?
  • 我没有测试代码,可能这段代码是多余的,但无论如何它是一个恒定的工作量。
【解决方案3】:

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

#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;
}

【讨论】:

    【解决方案4】:

    问题:在旋转的排序数组中找到最大的。该数组没有任何重复项: 解决方案:使用二分搜索。 理念:永远记住在有序旋转数组中,最大的元素总是在数组的左边。同样,最小的元素将始终位于数组的右侧。 代码是:

    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];
    
        }
    
    }
    

    【讨论】:

      【解决方案5】:

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

      int maxFinder(int[] array, int start, int end)
        {
          //Compute the middle element
          int mid = (start + end) / 2;
      
          //return the first element if it's a single element array
          //OR
          //the boundary pair has been discovered.
          if(array.length == 1 || array[mid] > array[mid + 1])
          {return mid;}
      
          //Basic Binary Search implementation
          if(array[mid] < array[start])
          {return maxFinder(array, start, mid - 1);}
          else if(array[mid] > array[end])
          {return maxFinder(array, mid + 1, end);}
      
          //Return the last element if the array hasn't been rotated at all.
          else
          {return end;}
        }
      

      【讨论】:

      • 上述程序适用于单元素数组和具有重复元素的数组。
      • { 0, 0, 1, 0 }?这将返回索引:查看问题中的示例。
      【解决方案6】:

      说到使用二分查找来解决这个问题,时间复杂度为 O(log2n)。我会这样做

      #include<stdio.h>
      #define ARRSIZE 200 
      int greatestElement(int* , int ) ;
      
      int main()  {
          int arr[ARRSIZE] ; 
          int n ; 
          printf("Enter the number of elements you want to enter in the array!") ;
          scanf("%d" , &n) ;
          printf("Enter the array elements\n") ;
          for(int i = 0 ; i < n ; i++)    {
              scanf("%d", &arr[i]) ;
          }
          printf("%d is the maximum element of the given array\n",greatestElement(arr,n)) ;
      }
      
      int greatestElement(int* arr, int n)    {
          int mid = 0 ;
          int start = 0 , end = n-1 ;
          while(start < end)  {
              mid = (start+end)/2 ;
              if(mid < n-1 && arr[mid] >= arr[mid+1]) {
                  return arr[mid] ;
              }
              if(arr[start] > arr[mid])   {
                  end = mid - 1 ;
              }
              else    {
                  start = mid + 1; 
              }
          }
          return arr[start] ;
      }```
      

      【讨论】:

        【解决方案7】:

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

        
            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];
            }
        
        

        【讨论】:

        • (不鼓励仅使用代码的答案。我发现的一个描述性标识符是size - 授予,n 有点传统,a 也是,如果这里不太适用的话。不要写,永远不要发布未注释/未记录的代码。(尝试实现“const正确性”。)
        • 请补充一些cmets,解决方案背后的想法
        猜你喜欢
        • 1970-01-01
        • 2019-06-01
        • 2023-03-26
        • 2011-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-26
        • 1970-01-01
        相关资源
        最近更新 更多