注意边界的变化方式。一般是
high = mid + 1
但有的时候可能没有 +1,甚至有 -1.
二分法难点就在于开闭的决定。这题就把我难住了,主要是不知道 mid 是否 + 1。
有个参考
以这道题目来举例,以下的代码中定义 target 是在一个在左闭右闭的区间里,也就是[left, right] (这个很重要)。
这就决定了这个二分法的代码如何去写,大家看如下代码:
大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1。
class Solution {
public:int searchInsert(vector& nums, int target) {int n = nums.size();int left = 0;int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]while (left <= right) { // 当left==right,区间[left, right]依然有效int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2if (nums[middle] > target) {right = middle - 1; // target 在左区间,所以[left, middle - 1]} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,所以[middle + 1, right]} else { // nums[middle] == targetreturn middle;}}// 分别处理如下四种情况// 目标值在数组所有元素之前 [0, -1]// 目标值等于数组中某一个元素 return middle;// 目标值插入数组中的位置 [left, right],return right + 1// 目标值在数组所有元素之后的情况 [left, right],这是右闭区间,所以 return right + 1return right + 1;}
};
这是别人的代码,但是有个问题,他定义了“target在左闭右闭的区间里,[left, right]”,那你为什么最后返回的是“right + 1”?
本质上是维护一个范围(当然还有数在范围之外的情况,另外讨论就好),关键是范围的正确缩小:就是你要插入的位置一定要在这个范围之内,所以当你调整范围的时候要慎重,别把正确的序号给排除了就好。
大概有这几种情况:
然后你再加上“数在范围之外的情况”,应该就好了。代码如下
int searchInsert(vector& nums, int target) {int low = 0, high = nums.size() - 1;// 数在范围之外的情况if (target <= nums[low]) return 0;if (target > nums[high]) return high+1;while (low < high){//if (high - low == 1) return high;int mid = low + (high - low) / 2;if (nums[mid] > target) high = mid ;if (nums[mid] < target) low = mid + 1;if (nums[mid] == target) return mid;}return high ;}
在掌握了范围的正确缩小算法后,我们再来看看前面的题目。
套用上面的思想,可以得到:
套用上面的思想,可以得到:
当二分法的思路清晰之后,代码就变得简洁了(从下面的代码一 -> 代码二)
// 代码一,罗里吧嗦,奇技淫巧,一通操作
int firstBadVersion1(int n) {if (n == 1) return 1;int low = 1, high = high = n;while (low < high){int mid = low + (high - low) / 2;if (isBadVersion(mid) == true){if (isBadVersion(mid - 1) == true){high = mid - 1;mid = low + (high - low) / 2;}else return mid;}else{if (isBadVersion(mid + 1) == true) return mid + 1;else{low = mid + 1;mid = low + (high - low) / 2;}}}return low;
}
// 代码二(简洁版)
int firstBadVersion3(int n) {if (n == 1) return 1;int low = 1, high = high = n;while (low < high){int mid = low + (high - low) / 2;if (isBadVersion(mid) == true) high = mid;//如果是ok,绝对不会在此之后,可能是这个,也可能是这个前的if (isBadVersion(mid) == false) low = mid + 1;}return low;
}
完工!