题目描述
1.题目分析
数组可以按旋转点为分界线分为左右两部分,如:
[4,5,6,0,1,2,3] = [4,5,6] + [0,1,2,3]
数组A:[4,5,6]; 数组B:[0,1,2,3]
· 注意本题为各不相同
于是我们便可以分两种情况讨论:** ①mid指在数组A,②mid指在数组B。**
1.1 情况①:
A B
[4, 5, 6] + [0,1,2,3]
↑ ↑ ↑
left mid right
mid在数组A,则nums[mid] >= nums[left]。出现这种情况,便让left = mid + 1,因为最小值一定在B中,这样可以缩小左边范围。
1.2 情况②:
A B
[4,5,6] + [0, 1, 2, 3]
↑ ↑ ↑
left mid right
mid在数组A,则nums[mid] < nums[left]。出现这种情况,便让right = mid, 之所以不是mid + 1, 是因为mid 可能就指向最小值,让right = mid就不会错过这种情况。
2. 我的尝试
2.1 错误尝试1 (边界溢出):
class Solution {
public int findMin(int[] nums) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= nums[left]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
}
2.1.1 报错信息:
索引超出边界
2.1.2 为什么会索引异常呢?
答:代码中left = mid + 1, 导致right = mid后,继续left继续右移了一位,导致边界异常。
2.2 错误尝试2 while (left < right):
class Solution {
public int findMin(int[] nums) {
int left = 0, right = nums.length - 1;
while(left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= nums[left]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
}
那我们不要让left = right后跳出循环 ,是否能解决边界溢出呢?
2.2.2 结果错误
结果全为最右边的值
2.2.3 为什么还是不对呢?
原来当left跑到B中后,我们的选择条件便不再成立, 如:
A B
[4, 5, 6] + [0, 1, 2, 3]
↑ ↑ ↑
left mid right
于是left会不断右移,导致最后输出的都是最右边的值。
2.2.4 如何解决呢?
出现这个问题的根本原因是nums[left]跑到B中去了,导致一直都只能进入如下的选择语句
if (nums[mid] >= nums[left]) {
left = mid + 1;
}
由于本题是为了找最小值,而最小值一定在B中,所以right指针一定不会跑到A里面去。如果拿nums[right]作为比较依据,便可以避免这种情况。
3. 代码实现
class Solution {
public int findMin(int[] nums) {
int left = 0, right = nums.length -1;
while(left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]) {
right = mid; // 不是mid - 1是防止 mid已经指向最小的 从而错过最小值
} else{
left = mid + 1;
}
}
return nums[left];
}
}
4. 思考
为什么这里不是while(left <= right) 呢?
答:因为我们返回的是nums[left]。若循环条件为while(left <= right),则跳出循环时,left = right = mid,都指向最小值,而这时候还得进行最后一次循环,使left = mid + 1。这时,left便跑到mid和right右边一位去了,因此若要用while(left <= right)条件必须返回 nums[right] 或者 nums[left - 1]。