1.题目
2. 代码
代码 1 while (left < right)
public int findMin1(int[] nums) {//输入的nums是不重复元素的数组。所以:mid = right 等价于 nums[mid] = nums[right]
int left = 0;
int right = nums.length - 1;
while (left < right) { // 循环条件判断,如果left == right,则循环结束
int mid = left + (right - left) / 2; //地板除,mid更靠近left
if (nums[mid] > nums[right]) { //中值 > 右值,最小值在右半边,收缩左边界
left = mid + 1; //因为中值 > 右值,中值肯定不是最小值,左边界可以跨过mid
} else if (nums[mid] < nums[right]) { //明确中值 < 右值,最小值在左半边,收缩右边界
right = mid; //因为中值 < 右值,中值也可能是最小值,右边界只能取到mid处
}
}
return nums[left]; //循环结束,left == right,最小值输出nums[left]或nums[right]均可
}
代码 2 while (left <= right)
public int findMin2(int[] nums) { //输入的nums是不重复元素的数组。所以:mid = right 等价于 nums[mid] = nums[right]
int left = 0;
int right = nums.length - 1;
int mid = 0;
while (left <= right) { // 循环的条件选为左闭右闭区间left <= right
mid = left + (right - left) / 2;
if (nums[mid] >= nums[right]) { // 注意是当中值大于等于右值时,
left = mid + 1; // 将左边界移动到中值的右边
} else if (nums[mid] < nums[right]){ // 当中值小于右值时
right = mid; // 将右边界移动到中值处
}
}
return nums[right]; // 最小值返回nums[right]
}
3. 分析
首先我们要考虑while循环条件是由什么决定的。显然,在二分法查找中,循环条件是由循环内部左右边界(left 和 right)的赋值决定的。并且不同的while循环条件下,最后return的结果也有所不同。
在分析代码之前,我们首先要明白mid是怎么得到的。由上述代码可知:
mid = left + (right - left) / 2;
因此,mid是由right和left经过地板除得到的中值索引,并且更靠近left(在right-left=1时,mid等于left)。了解这个是很重要的。
1.分析代码1
代码循环中的第一个条件判断是:
if (nums[mid] > nums[right])
这时根据题意,该条件下我们可知需要求的最小值一定在left的右边 (不包括left),故:
left = mid + 1;
其实上述left的赋值操作,根据逻辑来看,即使不包括left,似乎也可以是:“left = mid;”。但是,由于mid是地板除,在right和left的区间范围一步步缩小到最后的时候不能够使得left等于right,会陷入死循环。
代码循环中的第二个条件判断是:
if (nums[mid] < nums[right])
这时根据题意,该条件下我们可知需要求的最小值一定在right的左边 (包括right),故:
right = mid;
因为mid是地板除得到的,所以“right = mid;”语句能够在right和left的区间范围一步步缩小到最后的时候使得right等于left。
因而,基于上述赋值操作后,right和left相互靠近最终会相等的趋势来看,可以得出循环的结束条件是left == right,即while条件判断是while (left < right),并且最终返回的值为:
return nums[left];
或者
return nums[right];
这里返回不能是return nums[mid]; ,因为在循环内赋值的最后一步操作使得left = right,最后一步循环判断结束while循环,就不会再进入循环更新mid。
2.分析代码2
代码循环中的第一个条件判断是:
if (nums[mid] >= nums[right])
这个条件判断相较于代码1中多了 “=” 判断,这是为了使循环条件判断为:while (left <= right)。即,在left和right相等(相等时,left = mid = right)之后,再多执行一次“left = mid + 1; ”,使得left > right,最后跳出循环。并且最后的返回值为:
return nums[right];
或者
return nums[mid];
不能是 return nums[left] 是因为left在最后一步为了结束循环,已经大于right了。
除上述之外,其余原理 同代码1。