有个视频讲二分法很不错,然后记录一下。
数组中的数不重复
这种就很简单,不过用视频中的方法写一下就是
int BinarySearch(vector<int> nums, int target) {
//vector<int> nums({1,2,3,4,5,6,8,9});
//int target = 5;
int n = nums.size();
int l = -1;
int r = n;
while(l + 1 != r) {
int mid = l + (r - l) / 2;//一种防数值溢出的写法,正常就是(l + r) / 2
if(nums[mid] > target) {
r = mid;
}
else if(nums[mid] < target) {
l = mid;
}
else
return mid;
}
return -1;
}
这里呢,有一些与平常写的二分法不同的地方
1、为什么while(l + 1 != r) 而不是正常的while(l < r)?
答:这里呢,是想要l 与 r不会指向同一个数据,这个在之后的数组中的数有重复的时会有用
2、为什么l = -1, r = n,而不是 l = 0, r = n-1?
答:我们看下面这个图,首先咱们记住l + 1 != r
如果我要找的值是1,那么循环进行后,只有r会动,r指到2的时候停止,那么,l处就不会被检验到。同理,如果找的是9的话,循环进行,只有l动,但也不会检验到9这个数。出现错误了就。所以,当l = -1, r = n的时候,就不会有这个问题。
3、那这个时候mid是否始终处于[0,n)内?
答:我们看一下mid的最小值和最大值
这个是没问题的
如果能够进入循环,那么l + 1 != r, r最小就是1了
这个也没什么问题
如果能够进入循环,那么最后一次循环就是 n - 2 了
总结:mid正好在这个区域内
4、能不能写成l = mid + 1,或者 r = mid - 1
答:我之前写二分法就被这个搞得头疼。在这里,这个代码中,不能这样写。
因为这个代码中,是不会检验到 l 和 r 所指的数值的。
想一想:mid = l 的条件是 l + 1 = r,但这样根本不会进入循环体。
所以如果未检验过的数值(即未被mid指中的数)就不要让l或者r指向了。
当然还可以这样解释,如果我想找4,但mid指到了3,用到 l = mid + 1,l就是4,但是不会检验到l,这个程序就出错了。
数组的数重复
问题 | 对应位置 |
找到第一个大于等于5的元素位置 | 2 |
找到最后一个小于5的元素位置 | 1 |
找到第一个大于5的元素位置 | 5 |
找到最后一个小于等于5的元素位置 | 4 |
去解决这样的问题,l + 1 != r就有优势了,你可以用l或者r来输出相应位置。
这是找第一个大于等于5的元素位置代码。
int BinarySearch(vector<int> nums, int target) {
int n = nums.size();
int l = -1;
int r = n;
while(l + 1 != r) {
int mid = l + (r - l) / 2;
if(nums[mid] >= target) {
r = mid;
}
else if(nums[mid] < target) {
l = mid;
}
}
return r;
}
通过改变不等号和输出的l或者r。