给定一个旋转的排序数组,我怎样才能找到该数组中的最大值?
Posted
技术标签:
【中文标题】给定一个旋转的排序数组,我怎样才能找到该数组中的最大值?【英文标题】: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。
【问题讨论】:
好的,继续你的思路。回答面试问题不是要知道答案,而是要解决问题。 看看这个:***.com/questions/1878769/… 1.找到最小值。 2.返回左边的值。 我认为最佳复杂度是 O(log(n))。读取第一个、中间和最后一个值。如果 first[1, 5, 7, 6, 4, 10, 11]
,它没有排序——对吧?还是我误解了这个问题?如果我误解了,我认为您需要更好地解释数组的约束是什么。
【参考方案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<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,解决方案背后的想法以上是关于给定一个旋转的排序数组,我怎样才能找到该数组中的最大值?的主要内容,如果未能解决你的问题,请参考以下文章