描述
假设一个旋转排序的数组其起始位置是未知的比如:(0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2),你需要找到其中最小的元素。
注意事项
你可以假设数组中不存在重复的元素。
样例
给出[4,5,6,7,0,1,2] 返回 0
思路
- 找到一个分界点,以这个点为分界点将数组分为两部分,第一部分值大于分割点,第二部分值小于分割点,最小值为第二段区间的第一个点,本题要以数组最后一个元素作为数组的分界点
- 不能取第一个元素做分割点,当数组是没有旋转情况下,比如数组为1、2、3时,取第一个点作为分割点时数组分为两段分别是1和 2、3,程序返回第二段区间的第一个点是的是2,而正确结果应该是1。但如果取的是最后一个点 3 做分割,第一部分没有值,第二部分值为 1、2、3,则第二部分最小值为1,为最小值
- 不断二分最后锁定在断点两端的两个点上;但要考虑特殊情况比如正常的
01234567
顺序,start 和 end 两个点都小于 target,所以应该先判断 start 位置的元素
代码
public class Solution {
public int findMin(int[] nums) {
if (nums == null || nums.length == 0) {
return -1;
}
int start = 0;
int end = nums.length - 1;
int target = nums[nums.length - 1];
// target 即为将循环数组分为两部分的数值分界点(一部分大于target,一部分小于target)
while (start + 1 < end) {
int mid = start + (end - start) / 2;
/* 根据if条件判断mid是在循环数组的前面还是后面
* 二分法会使得start和end最终返回在分界点,但
* 到底是前面点还是后面点不确定,需要用if来检验
* 求最小值往前找总是没错的
*/
if (nums[mid] <= target) {
end = mid;
}
if (nums[mid] > target) {
start = mid;
}
}
// nums[start] < target 说明数组未旋转
// nums[end] = target 说明数组值都是相同值
if (nums[start] <= target) {
return nums[start];
}
else {
return nums[end];
}
}
}
最后这个 double check 在特殊情况即 12345 这种未旋转顺序下可能出现 start <= target
如果不考虑这种特殊情况,double check 可以直接写成 return nums[end]