把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof
解题思路
分情况讨论
- 如果整个数组是递增的, 则首元素小于尾元素, 此时直接返回第一个元素
- 整个数组不是递增的, 用二分法
- 设置三个索引, 分别是最左、最右、中间
- 循环判断
- 如果"左边的数小于等于中间的数", 则说明左边到中间这区间的数是递增的, 最小数不在这里, 将左索引赋值为中间索引
- 如果"右边的数大于等于中间的数", 则说明中间到右边这区间的数是递增的, 最小数不在这里, 将右索引赋值为中间索引
- 但是要注意一个特殊情况, 如果"左边的数 == 中间的数 == 右边的数", 此时无法判断最小数所在区间, 只能顺序遍历
代码
class Solution {
public int minArray(int[] numbers) {
if (numbers == null || numbers.length == 0) {
return 0;
}
int length = numbers.length;
int left = 0, right = length - 1, mid;
// 检查左边的数是否大于等于右边的数
while (numbers[left] >= numbers[right]) {
// 如果两个索引相邻, 则最小数一定在右边
if (right - left == 1) {
return numbers[right];
}
mid = (left + right) / 2;
// 当左索引的数和右索引的数和中间索引的数相等
// 此时无法知道最小数位于哪个半区, 只能顺序查找
if (numbers[left] == numbers[right] && numbers[left] == numbers[mid]) {
return minInOrder(numbers, left, right);
}
// 如果左边的数比中间小, 则说明left到mid的区间的数是递增的
// 所以最小数不在这个范围, 将范围缩小(左索引赋值为中间)
if (numbers[left] <= numbers[mid]) {
left = mid;
}
// 同理, 右边的数大于中间, 则mid到right的区间的数是递增的
// 最小数不在这个范围内, 将范围缩小(右索引赋值为中间)
else if (numbers[right] >= numbers[mid]) {
right = mid;
}
}
// 根本没进去循环, 说明整个数组是递增的
return numbers[0];
}
// 顺序查找的方法
public int minInOrder(int[] nums, int left, int right) {
// 现将结果设定为第一个数
int result = nums[left];
for (int i = left + 1; i <= right; i++) {
// 找到第一个比result小的数就赋值然后返回
if (result > nums[i]) {
result = nums[i];
break;
}
}
return result;
}
}