记这篇笔记的原因是我对二分法书写缺少一种标准和思维模式。
二分法理解起来虽然简单,但是在书写的时候有很多细节需要注意,比如二分的起始终止条件是什么(while(i!=j) while(i<j) while(i<=j))?二分范围的缩进要如何实现(是头尾同时缩进还是只缩进一端)?
这些细节上的问题要慢慢处理的话很麻烦,我希望积累一些标准的二分法的书写格式,这样在限定时间内完成题目的时候可以更方便一点。
场景一
实现一个简单的二分查找:对于升序数组,判断目标数target是否在数组中。
例如对于数组nums[1,4,5,7,8],8在数组中,而6不在数组中。
代码实现:
public boolean binarySearch(int[] nums,int target) {
int mid,i=0,j=nums.length;
while(i<=j) {
mid=(i+j)/2;
if(target==nums[mid]) return true;
else if(target>nums[mid]) i=mid+1;
else j=mid-1;
}
return false;
}
一些结果示例:
代码说明
首先我们需要在数组中找到的是刚好等于目标数的数字,这里就提示了二分范围i和j缩进的形式:
当target>nums[mid]时:i=mid+1
当target<nums[mid]时:j=mid-1
同时二分查找循环的条件是while(i<=j)
而不是while(i<j)
。因为如果目标数如果刚好是在边界(即目标数 == nums[i]或者目标数 ==nums[j])那最终会搜索到i==j
的时候才会找到目标数。
如果不检查最后一遍i==j
的情况,二分查找就会失败。
场景二
有一个数组,要求当给一个目标数时,让目标数替换数组中刚好大于等于它的那个数。
例如对于nums[1, 3, 6]和target=2,由于3刚好是大于等于2的那个数,替换之后数组变为[1,2,6].
再例如对于数组nums[1,3,6,8,9,13,22],给定目标数为7后,可以替换掉数组中的8,数组变为[1,3,6,7,9,13,22]
代码实现:
public void binarySearch(int[] nums,int target) {
int mid,i=0,j=nums.length-1;
while(i!=j) {
mid=(i+j)/2;
if(target>nums[mid])
i=mid+1;
else
j=mid;
}
nums[i]=target;
}
一些结果示例:
代码说明
首先我们需要在数组中替换的是大于等于目标数的数字,所以这里就提供了二分范围起点i和终点j的缩进形式为:
当target>nums[mid]时:我们要替换的数不可能是nums[mid],因为我们需要替换的数是大于等于target的数,而nums[mid]<targer。所以缩进方式为i=mid+1
当target<nums[mid]时:缩进方式为j=mid而不是。因为nums[mid]有可能刚好就是我们需要替换的那个数,所以不能是j=mid-1,因为这样可能会跳过我们要找的数。
这个场景的应用在leetcode第300题上:
问题:https://leetcode.com/problems/longest-increasing-subsequence/submissions/
解法:https://segmentfault.com/a/1190000003819886