一.概念
二分查找法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。它的基本思想是:(这里假设数组元素呈升序排列)将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止;如 果x<a[n/2],则我们只要在数组a的左半部继续搜索x;如果x>a[n/2],则我们只要在数组a的右 半部继续搜索x。
二.在题目中掌握思想(代码中会有注释)
ps:题目均选自力扣
1.二分查找
题目链接:704. 二分查找 - 力扣(LeetCode)
基本分析:可以通过遍历一遍查找target,这样时间复杂度是O(n),而二分是O(logn),而在二分的基础使用中应该注重的就是边界问题,常见的是左闭右开和左闭右闭,什么时候l<=r,什么时候l<r,什么时候l=m+1等等。下面会有两版代码分别讲解左闭右开和左闭右闭
左闭右闭:
class Solution {
public int search(int[] nums, int target) {
int l = 0;//定义左边界
//定义右边界,此时我们是以nums.length-1为右边界,
//那么我们是认为右边界是有意义的,请牢记这一点
int r = nums.length-1;
int flag = -1;//用来标记目标的索引
while(l<=r){//因为右边界是有意义的,所以i可以等于j
int m = (l+r)>>>1;//相当于(i+j)/2,在java中整数间的除法是向下取整
//nums[m]>target,则索引m右边的元素都大于target,由于m不可能是target且j有意义,所以j=m-1
if(nums[m]>target)r = m-1;
//nums[m]<target,则索引m左边的元素都小于target,由于m不可能是target且i有意义,所以i=m+1
else if(nums[m]<target) l=m+1;
//相等,则是目标元素,标记并跳出循环
else {
flag = m;
break;
};
}
return flag;
}
}
左闭右开:
class Solution {
public int search(int[] nums, int target) {
int l = 0;//定义左边界
//定义右边界,此时我们是以nums.length为右边界,
//由于右边界超出了数组索引范围,那么我们是认为右边界是没有意义的,请牢记这一点
int r = nums.length;
int flag = -1;//用来标记目标的索引
while(l<r){//因为右边界是没有意义的,所以i不等于j
int m = (l+r)>>>1;//相当于(i+j)/2,在java中整数间的除法是向下取整
//nums[m]>target,则索引m右边的元素都大于target,由于m不可能是target且j没有意义,所以j=m
if(nums[m]>target)r = m;
//nums[m]<target,则索引m左边的元素都小于target,由于m不可能是target且i有意义,所以i=m+1
else if(nums[m]<target) l=m+1;
//相等,则是目标元素,标记并跳出循环
else {
flag = m;
break;
};
}
return flag;
}
}
现在你应该对二分有了一个基本的认识了
2.在排序数组中查找元素的第一个和最后一个位置
题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
基本分析: 在左闭右闭中,如果我把判断条件改为nums[m]>=target,r=m-1亦或者nums[m]<=target,l=m+1会发生什么变化?
我们就拿nums[m]>=target,r=m-1来讲述,则循环条件中代码如下:
while(l<=r){
int m = (l+r)>>>1;
if(nums[m]>=target)r=m-1;
else l = m+1;
}
上述代码:我们假设循环来到了最后一步,也就是l=r=m,目标元素(可以是多个)存在于数组内我们分为两种情况:
a.是因为l=m+1导致循环跳出
如果走的是第一个条件,也就是nums[m]>=target,由于目标值存在于数组内,什么情况下不走第一个?也就是上一步中nums[m]==target,然后走了第一个条件了。那我们就可以推断出m+1就是目标元素最左边的所以,由于l+1了,所以就是l;
b.是因为r=m-1导致循环跳出
说明走的是第一个条件,由于是最后一步,不可能存在m索引左边存在元素大于等于target,因为此时i都等于m了,也就是说,最后一步是nums[m]==target,m就是目标元素最左边的位置,所以也就是l;
思想知道了,代码如下:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] arr2 = { -1, -1 };
if (nums.length == 0)
return arr2;
int i1 = 0, i2 = 0;
int j1 = nums.length - 1, j2 = nums.length - 1;
while (i1 <= j1) {
int m = (i1 + j1) >>> 1;
if (nums[m] >= target)
j1 = m - 1;
else
i1 = m + 1;
}
if (!(i1 <= j2 && nums[i1] == target))
return arr2;
arr2[0] = i1;
while (i2 <= j2) {
int m = (i2 + j2) >>> 1;
if (nums[m] <= target)
i2 = m + 1;
else
j2 = m - 1;
}
arr2[1] = j2;
return arr2;
}
}