二分查找法
使用二分查找法的前提:(1)数组为有序数组.
(2)数组中无重复元素.
二分的两种写法:
方法一:[left,right]
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while (left <= right)
{
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;
}
};
方法二:[left,rigght)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while (left <=right)
{
int mid=(left+right)/2;
if(nums[mid]>target)
right=mid;
else if(nums[mid]<target)
left=mid+1;
else
return mid;
}
return -1;
}
};
核心代码(递归)
int binsearch(int left,int right)
{
if(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]==target)
return mid;
if(nums[mid]>target)
return binsearch(left,mid-1);
if(nums[mid]<target)
return binsearch(mid+1,right);
}
return 0;
}
核心代码(循环)
int f=-1;
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]==target)
{
f=mid;
break;
}
if(target<nums[mid])
right=mid-1;
if(target>nums[mid])
left=mid+1
}
if(f==-1)
cout<<"没找到";
else
cout<<f<<endl;
以下是力扣二分法题目的常见解题思路:
704. 二分查找
- 初始化左右指针 left 和 right ,分别指向数组两端。
- 在 left <= right 的循环中,计算中间索引 mid 。
- 比较 nums[mid] 与 target ,若相等则返回 mid ;若 nums[mid] > target ,则更新 right = mid - 1 ;若 nums[mid] < target ,则更新 left = mid + 1 。
- 循环结束未找到则返回-1。
35. 搜索插入位置
- 同样以 left 和 right 初始化数组两端。
- 循环条件为 left <= right ,计算 mid 后,若 nums[mid] >= target ,则 right = mid - 1 ,否则 left = mid + 1 。
- 循环结束后, left 所指位置就是目标值应插入的位置。
34. 在排序数组中查找元素的第一个和最后一个位置
- 先通过二分法找目标值第一个位置,当 nums[mid] >= target 时,更新 right = mid ,循环结束后 left 可能是第一个位置,需再判断 nums[left] 是否为 target 。
- 找最后一个位置时,当 nums[mid] <= target 时,更新 left = mid ,循环结束后,若 nums[right] 是 target , right 就是最后一个位置。
69. x的平方根
- 令 left = 0 , right = x ,在 left <= right 循环中计算 mid 。
- 若 mid * mid <= x && (mid + 1) * (mid + 1) > x ,则 mid 就是所求平方根;若 mid * mid > x ,更新 right = mid - 1 ;否则更新 left = mid + 1 。
367. 有效的完全平方数
- 类似求平方根, left 设为1, right 设为 num 。
- 循环中计算 mid ,若 mid * mid == num ,则返回 true ;若 mid * mid > num ,更新 right = mid - 1 ,否则更新 left = mid + 1 。循环结束未找到则返回 false 。
162. 寻找峰值
- 初始化 left = 0 , right = nums.length - 1 。
- 当 left < right 时,计算 mid ,若 nums[mid] > nums[mid + 1] ,说明峰值在左半部分,更新 right = mid ;否则峰值在右半部分,更新 left = mid + 1 。
- 循环结束后, left 指向的就是一个峰值位置。
判断 right 应赋值为 mid 还是 mid - 1
关键在于二分查找的目标以及当前中间值与目标值的关系,具体可从以下几方面判断:
- 查找精确值且数组元素唯一:当目标是在有序数组中查找某个精确值,且数组元素唯一时,若 nums[mid] > target ,说明目标值在左半部分,此时应将 right 赋值为 mid - 1 。因为 nums[mid] 已经大于 target ,它不可能是目标值,所以下一轮查找范围应不包含 mid ,如704. 二分查找就是这种情况。
- 查找左边界或最大不超过目标值的元素:若要查找目标值在数组中的左边界,或者是查找数组中最大的、不超过目标值的元素时,当 nums[mid] >= target ,应将 right 赋值为 mid 。这是为了让查找范围继续向左侧收缩,且保留 mid 位置,因为 mid 位置有可能就是左边界,或者是符合条件的最大元素,如35. 搜索插入位置、34. 查找元素第一个位置就需这样处理。
- 查找右边界或最小超过目标值的元素:当任务是查找目标值的右边界,或者是要找到数组中最小的、超过目标值的元素时,若 nums[mid] <= target ,则应将 right 赋值为 mid - 1 。这样能使查找范围向右收缩,同时排除 mid 位置,因为 mid 位置及左侧元素已不满足“右边界”或“最小超过目标值”的条件,例如34. 查找元素最后一个位置时就遵循此规则。