题目来源:https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&&tqId=11159&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
题解
解题思路:
- 首先,这肯定是一个查找问题,可以直接暴力排序然后找出最小值,但是肯定不太符合出题意愿。
- 因为,旋转数组的原数组是非递减排序的,即原数组是升序的,数字可以重复。
- 所以,将数组的开始部分搬到数组的末尾后,可以发现:数组左部分 > 数组右部分,即最小值在右部分第一个数
- 因为数组可以看作有序,所以可以用二分查找,但是没有目标值target,即没有确定最小值,这就是这道题的难点,所以这里不是target与中间值比较,而是中间值与最右边值比较
-
- 当
array[mid] < array[high]
,可以知道中间值array[mid]
在右部分,但是mid
不一定是最小,所以high
左移,high = mid
,注意这里high
不是mid-1
是避免mid
刚好是最小值 - 当
array[mid] > array[high]
,可以知道中间值array[mid]
在左部分,所以low
右移,low = mid + 1
- 当
array[mid] = array[high]
遇到重复数字,high
左移一位 - 最后,当
low = high
时,就是最小值
- 当
代码实现
/**
* 二分查找
*/
public class Solution {
public int minNumberInRotateArray(int[] array) {
// 判空处理
if (array == null || array.length == 0) {
return 0;
}
int low = 0;
int high = array.length - 1;
// 这里是为了找到最小值,low = high时为最小值,所以可以不用比较 low = high
while (low < high) {
// 这里的 mid 没有使用 mid=(low+high)/2,是避免oj中的加法发生溢出
int mid = low + (high - low) / 2;
// 如果 array[mid] < array[high],array[mid]是右部分元素,所以high左移
if (array[mid] < array[high]) {
// 这里与原来二分查找的区别,不是high=mid-1,是为了避免 mid 刚好是最小值
high = mid;
}
// 如果 array[mid] > array[high],array[mid]是左部分元素,所以low右移
else if (array[mid] > array[high]) {
low = mid + 1;
}
// 如果 array[mid] = array[high] 遇到重复数字,high左移一位
else {
high -= 1;
}
}
return array[low];
}
}