算法学习之 二分
一、算法思想
二分算法是不断将某一个区间一分为二,并将不符合要求的区间舍弃,达到快速缩小搜索范围最终找到目标值或确定目标不存在的高效算法。
模板例题:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
因为题中的数组为有序数组(非递减顺序排列),我们可以使用二分不断缩小查找范围直到得到结果。
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {if(nums.size()==0) return {-1,-1};int left=0,right=nums.size()-1;//左int begin;while(left<right){int mid=(left+right)/2;if(nums[mid]<target){left=mid+1;}else right=mid;} if(nums[left]!=target) return {-1,-1};begin=left;//右left=0;right=nums.size()-1;while(left<right){int mid=(left+right+1)/2;if(nums[mid]<=target){left=mid;}else right=mid-1;} return {begin,left};}
};
二、注意事项
二分算法通常需要注意以下三点:
1、循环条件(while(left<=right) 或者 while(left<right) 以及左右界限的移动方式)
2、中点的合理选取 (mid=(left+right)/2 或者 mid=(left+right+1)/2)
3、二分答案的正确性判断
4、使用二分的前提是被搜索区间有序
上面三项注意点需要结合具体题目分析。特别要注意的是,前面两点是较难判断的点也是我们常常重点考虑的点。因此第三点就更容易被我们所忽略,而一旦第三点发生错误整个代码就错了!
所以一定要考虑二分答案是否符合题目要求!
三、练习
1、P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 (luogu.com.cn)
上面题目中,我们通过二分查找找到最短的锯片长度。
在哪里进行二分查找呢?就在从题目中推断出的锯片长度范围(0~树中最大长度)中查找。
再处理下循环条件,这里的循环条件需要与目前所选择锯片长度是否符合题目要求相关联。
当然别忘了判断一下在所写逻辑中得到的二分答案是否正确。
再看看另一道题:P2678 [NOIP 2015 提高组] 跳石头 - 洛谷 (luogu.com.cn)
这道题目是要在有限的行动次数下让最短跳跃距离尽量的大,我们同样可以在最短跳跃距离的范围(0~终点距离起点的距离)中通过二分来查找到符合题目要求的最短距离。
具体点说就是假设在二分查找中的当下的值就是最短跳跃距离,通过判断要想达成这一要求需要多少行动点数(可用双指针求得)来决定循环条件。
四、总结
当题目中要求某某最大/最小且伴随着有序序列(可能不会明白的告诉我们,需要自己发现或者处理)时就有可能需要用到二分算法(当然还可能是贪心、动态规划……),这时我们就可以把解题思路往哪方面靠。
二分算法的难点就是循环条件的判断以及二分中点的选取,当然也别忘了检查二分答案的正确性。