大佬总结
分析
「二分查找」虽然看起来有很多种写法,「递归」和「非递归」,「非递归」又有好几种写法:while (left <= right)、while (left < right)、while (left + 1 < right)。但核心的思想就一个:逐渐缩小问题规模。我们在学习和练习的时候需要 首先着眼于掌握算法的思想,而不该去纠结二分的几种写法的区别和细节,这样会让自己更乱;
如何分析,利用单调性(绝大多数二分查找问题利用的是单调性,也有一些例外)或者题目本身蕴含的可以逐渐缩小问题规模的特性解决问题,
while(left <= right) 这种写法可以认为我们在循环体内部直接查找元素,把当前搜索区间分为三个部分。
while(left < right) 这种写法表示在循环体内部排除元素,把当前搜索区间分为两个部分。
这种思路可以很形象地理解为「两边夹」,在解决复杂问题的时候,会使得思考的过程变得简单。
当 while (left < right) 时,
对应的更新式是 left = middle + 1 , right = middle
当 while (left <= right) 时,
对应的更新式是 left = middle + 1,right = middle - 1
在 写 if 语句的时候,通常把容易想到的,不容易出错的逻辑写在 if 的里面,这样就把复杂的、容易出错的情况放在了 else 的部分,这样编写代码不容易出错。
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
例子
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
解
import java.util.Arrays;
/**
* @Auther: ljm
* @Date: 2021/06/08/9:17
* @Description:
*/
public class lc34 {
public static void main(String[] args) {
int[] n =new int[]{5,7,7,8,8,10};
int array[] = new Solutionlc34().searchRange(n,8);
// for(int i=0;i<array.length;i++){
// System.out.println(array[i]);
// }
//打印数组
System.out.println(Arrays.toString(array));
}
}
class Solutionlc34 {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[]{-1,-1};
int r = helper(nums,target);
if((r-1)>=0 &&nums[r-1]==target){
res[1] = r-1;
int lf = helper(nums,target-1);
res[0] = lf;
}
return res;
}
public int helper(int []nums,int target){
int left = 0,right = nums.length-1;
//寻找右边届
while(left<=right){
int mid = (right - left)/2+left;
if(nums[mid]<=target) left = mid+1;
else right = mid-1;
}
return left;
}
}