问题
定义局部最小的概念.
- arr长度为1时, arr[0]是局部最小
- arr长度为N(N > 1)时, 如果arr[0] < arr[1], 那么arr[0]是局部最小
- 如果arr[N-1] < arr[N-2]时, 那么arr[N-1]是局部最小
- 如果0 < i< N-1, 既有 arr[i] < arr[i – 1], 又有arr[i] < arr[i + 1], 那么arr[i]是局部最小
给定一个无序数组arr, 已知arr中任意两个相邻的数都不相等. 写一个函数, 只需返回arr中任意一个局部最小出现的位置即可.
分析
顺序遍历是最直接的方法, 时间复杂度是O(n).
下面介绍一种时间复杂度O(logN)的方法.
基本思想: 二分查找.
- 如果数组长度为0, 返回 -1
- 如果数组长度为1, 返回0
- 如果arr[0] < arr[1], 返回0
- 如果arr[size – 1] < arr[size – 2], 返回size – 1
- 从arr[1]到arr[size – 2]开始二分查找
- left = 1, right = size – 2
- mid = left + (right – left) / 2
- 如果arr[mid] > arr[mid – 1], 那么在mid左侧,必然存在局部最小位置, 令righ = mid – 1. 继续二分查找
举个栗子: 对于数组 {x, 2, 3, 5, 6}, mid = 2, arr[2] > arr[1], 如果处于位置0的x值小于2, 那么满足第三步的条件, 所以x值一定大于2, 位置1即是满足条件的位置. - 同样可以分析, 如果arr[mid] > arr[mid + 1], 那么在mid右侧, 必然存在局部最小位置, 令left = mid + 1. 继续二分查找.
- 如果不满足5.3, 5.4, 那么mid所在位置即是满足条件的位置.
实现
class Solution
{
public:
int partialMinimum(const std::vector<int>& nums)
{
int size = nums.size();
if (size == 0)
{
return -1;
}
if (size == 1 || nums[0] < nums[1])
{
return 0;
}
if (nums[size - 1] < nums[size - 2])
{
return size - 1;
}
int left = 1;
int right = size - 2;
int result = -1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > nums[mid - 1])
{
right = mid - 1;
}
else if (nums[mid] > nums[mid + 1])
{
left = mid + 1;
}
else
{
result = mid;
break;
}
}
return result;
}
};