题目链接:
题目描述:
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1≤n≤100001 \le n \le 100001≤n≤10000,数组中任意元素的值: 0≤val≤100000 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1)O(1) ,时间复杂度:O(logn)O(logn)O(logn)
思路:
死抓题目给的非递减(递增或相等)数组的条件,旋转后的数组分为前半部分和后半部分,前半部分和后半部分都是非递减数组,后半部分的第一个数就是要求的目标值(即最小的数)。可以定义两个指针,一个指向头,一个指向尾,二分查找,找的的数大于头就是属于前半部分,直接砍掉一半,反之则属于后半部分,砍掉另一半。
细节:三者相等的情况下,无法判断mid属于前半部分还是后半部分,所以逐个判断寻找最小的值。
代码实现如下:
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if (array.length == 0) return 0;
int left = 0;
int right = array.length - 1;
//如果数组没有旋转,直接返回array[mid]。
int mid = 0;
//旋转过的数组才符合条件进入数组
while (array[left] >= array[right]) {
//数组长度为2时的情况,由于非递减数组旋转过,肯定是右边的更小。
if (right - left == 1) {
mid = right;
//直接退出循环,不需要参与后面的计算
break;
}
//二分查找,选定中间值
mid = (left + right) / 2;
//当旋转过后的数组左边等于右边等于中间的值,无法判断中间值在前半部分还是后半部分。
//直接遍历数组找出最小值,找到后可以直接返回。
if (array[left] == array[right] && array[left] == array[mid]) {
for (int i = left + 1;i < right;i++) {
if (array[i] < array[mid]) {
mid = i;
}
}
return array[mid];
}
//当左边值小于中间值,由于前半部分和后半部分都是非递减数组,说明中间值在旋转后的数组前半部分,
//而我们要找的最小值在后半部分的第一个数,所以直接让left == mid,砍掉一半。
if (array[left] <= array[mid]) {
left = mid;
} else {
right = mid;
}
}
return array[mid];
}
}