006_二分查找
二分查找不难,但是很容易出错
(easy 704. 二分查找)
📌1 递归
递归要注意target的值在哪个范围, 如果target小于最小值或者大于最大值
📌2 循环
class Solution {
public:
int search(vector<int>& nums, int target) {
//左右指针采取 双闭区间
int left = 0;
int right = nums.size() - 1; //注意
while(left<=right) //注意 明确搜索空间这个概念.如果是双闭区间,[left,left]非空,要再循环一次;如果左闭右开, [left,left)是空,<就行了
{
int mid = (left+right)/2;
if(nums[mid]>target) right = mid - 1; //注意 这里的关键就是下一个搜索区间也是 双闭区间
else if(nums[mid]<target) left = mid + 1;
else return mid;
}
return -1;
}
};
📌总结
- 尾递归都可以用循环解决
- 如果采取 双闭区间, 上例中的这个num[mid], 奇数就是中间那个, 偶数就是中间两个靠左边那个
📌 两种区间
上例假设 nums.size() = n 用索引取值的话就是取到: 0, 1, ... , n-1
我们通常使用以下两种方法来表示这个取值范围:
1.双闭区间[0,n-1]
,即两个边界都包含自身;在此方法下,区间 [0,0]
仍包含个元素;(建议使用)
2.左闭右开[0,n)
,即左边界包含自身、右边界不包含自身;在此方法下,区间 [0,0)
不包含元素.
C++迭代器是使用左闭右开区间 [)
的. 比如erase(), 去除一个左闭右开区间, 返回右边的元素.
在“双闭区间”表示法中,由于对左右两边界的定义相同,因此缩小区间的i和j的处理方法也是对称的,这样更不容易出错。因此,建议采用“双闭区间”的写法。(Hello 算法)
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res{-1,-1};
if(nums.size()==0) return res;
//开始位置
int left=0;int right=nums.size()-1;
while(left<=right)
{
int mid = left+(right-left)/2;
if(nums[mid]<target) left=mid+1;
else right=mid-1; //继续锁定左边界
}
if(left==nums.size()) res[0]=-1;
else res[0]= nums[left]==target?left:-1;
//结束位置
left=0;right=nums.size()-1;
while(left<=right)
{
int mid = left+(right-left)/2;
if(nums[mid]<target) left=mid+1;
else left=mid+1; //继续锁定右边界
}
if(left-1<0) res[1]=-1;
else res[1]=nums[left-1]==target?left-1:-1;
return res;
}
};
注意越界的判断