二分查找
- 二分查找是大多数人第一个接触到的算法,很多人都认为只有有序的数组可以使用二分查找,但这种思想其实是错误的,二分查找是可以用于拥有二段性的数组,而且二分算法是由模板做参考的,所以只要掌握就可以解决大多二分查找的问题
题目
class Solution {
public:
//二分查找算法不一定是要使用严格的升序或者降序,只要区间可以划分为二段性就可以使用
vector<int> searchRange(vector<int>& nums, int target) {
//其实可以分别寻找第一个位置和最后一个位置
int left = 0, right = nums.size()-1, sz = nums.size(), mid = 0;
vector<int> ret;
//寻找左边界
while(left <= right){
mid = left + (right-left+1)/2;
if(nums[mid] == target && (mid == 0 || nums[mid-1] != target)){
ret.emplace_back(mid);
break;
}
if(nums[mid] > target || nums[mid] == target) right = mid-1;
if(nums[mid] < target) left = mid+1;
}
//寻找右边界
left = mid, right = nums.size()-1;
while(left <= right){
mid = left + (right-left+1)/2;
if(nums[mid] == target && (mid == sz-1 || nums[mid+1] != target)){
ret.emplace_back(mid);
break;
}
if(nums[mid] > target) right = mid-1;
if(nums[mid] < target || nums[mid] == target) left = mid+1;
}
if(ret.empty()){
return {-1,-1};
}
return ret;
}
};
这是我最开始写的代码,最开始其实我不屑于看什么模板,感觉这些用if else 语句都可以解决,但多做了几道发现if else语句会让你的代码变得非常的臃肿,而且有一种面向测试用例编程的感觉,所以在在这里我想斗胆给各位好好讲一下二分查找算法
查找左端点
- 首先,这里我们将区间分为小于和大于等于,这样二段性就出来了,你可能会问,为什么不划分为小于等于和大于这两个区间,因为我们查找的是左端点,划分为小于等于和大于还怎么找左端点
算法原理
1. x < t(target) left = mid +1 (这里可以=mid+1因为左区间划分是小于,所以最终结果一定不在左区间,所以left = mid + 1) 1. x >= t right = mid (这里不可以是 right = mid-1)因为如果right = mid-1的话很可能会出现right超出区间的情况,这样就会导致没法判断到结果
细节处理
-
循环条件:
- left < right
为什么不是left <= right,大家可以看下图,如果此时ret > target这是就会执行第二种语句,right=mid,那就会造成死循环
-
找中点的操作
mid = left + (right-left)/2 这种情况是如果只有两个元素会找左边的那一个
为什么不是 mid = left + (right-left+1)/2
大家看下图如果是找右边的且nums[right]>=t那不是又会出现死循环吗。
寻找右端点
- 这里的基本操作都是一样的,出了找中点还有算法(left与right的移动)不一样,所以不多赘述,各位可以自行画图处理,下面给大家一个模板,大家可以参考。
完整代码
class Solution {
public:
//二分查找算法不一定是要使用严格的升序或者降序,只要区间可以划分为二段性就可以使用
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.empty()) return{-1, -1};
//其实可以分别寻找第一个位置和最后一个位置
int left = 0, right = nums.size()-1, sz = nums.size(), mid = 0, begin = 0;
//寻找左边界
while(left < right){
mid = left + (right-left)/2;
if(nums[mid] >= target) right = mid;
if(nums[mid] < target) left = mid+1;
}
//判断
if(nums[left] != target) return {-1, -1};
begin = left;
//寻找右边界
right = nums.size()-1;
while(left < right){
mid = left + (right-left+1)/2;
if(nums[mid] > target) right = mid-1;
if(nums[mid] <= target) left = mid;
}
return {begin, right};
}
};