算法学习记录 二分查找

是记录,会有错误,欢迎各位大佬在评论区发表意见

数组

二分查找

这里是以升序的为例
给出我学习的疑惑

  1. 为什么有时候mid是left+1,有时候又是left-1?
  2. 为什么有时候mid是right+1,有时候有是right-1?
    回到区间定义这里,当while是这样的时候
	while(l<r)
		.....

nums[mid]的时候,target是在mid的右边,所以我们需要调整左边界。

注意:由于区间定义是左闭右开的,left指向的是包含target这个情况的,因为nums[mid]

比如:当nums[mid]>target的时候,target在mid的左边,右边界可以一个必定不等于target的(这里我感觉我理解错了)边界需要更新为[left,mid)

当while是左闭右闭区间的时候,即[left,right]

nums[mid] < target, target在mid的右边,边界需要更新为[mid+1,right],left为何更新为mid+1?因为区间定义要求left和right指向的值都要包含等于target的这个情况,mid都比target小,那么mid前面的都比target小

当nums[mid] > target, target在mid的左边,边界需要更新为[left,mid-1],原因上同

LeetCode 35. 搜索插入位置

这题暴力也是可以的哈
二分的性质(第二条是试出来的):

  1. 二分一定有结果,若是找值(目标存在)那么一定能找到其下标,若目标不存在,那么找到的是最接近于目标的
  2. 退出循环的时候,左闭右闭区间left=right+1,若是左闭右开则是left=right

第二条试探过程
nums={1,8}; target = 0
left=0,right=1 初始化
left=0,right=0
left=0,right=-1 出循环了

nums={1,8};target = 7
left=0,right=1 初始化
left=1,right=1
left=1,right=0 出循环了

nums ={1}; target = -1
left=0,right=0 初始化
left=0,right=-1出循环了
试探的过程中发现出循环的left值就是我们想要的结果

LeetCode 34

提示:我这题是以左闭右开区间写的

找第一次出现的位置

int search(int[] nums,int target){
	int l =0,r=nums.length;
	while(l<r){
		int mid = (l+r)/2;//这里我没有考虑溢出的情况
		if(nums[mid] >=target)//表明第一次出现的位置一定是在mid或者mid前面
			r=mid;
		else // nums[mid] < target target第一次出现的位置一定是在mid的后面
			l=mid+1;
	}
	return l;
}

找最后一次的,这个我主要是边界范围极大概率会出错,二分寻找最后一个出现的位置的边界更新的规则如下

  1. a[mid]>target,表明mid后面的数必定是大于target的,所以r=mid-1
  2. a[mid]<=target,表明target在mid的后面(包含mid),所以l=mid
  3. 当l=r时,区间就只有一个元素,这个就是target最后一次出现的位置
    乍一看这好对啊,但是模拟一次哈

nums = {1,2,2,2,2,9},target=2
0,1,2,3,4,5
第一次: l=0,r=5,mid = (l+r)/2=2,a[mid]=2,条件2满足,l更新为mid也就是2

第二次:l=2,r=5,mid=(l+r)/2=3,a[mid]=2,条件2成立,l更新为mid也就是3

第三次:l=3,r=5,mid=(l+r)/2=4,a[mid]=2,条件2成立,l更新为mid也就是4

第四次:l=4,r=5,mid=(l+r)/2=4,a[mid]=2,条件2成立,l更新为mid也就是4 这里废了

第五次;l=4,r=5,mid=(l+r)/2=4,a[mid]=2,条件2成立,l更新为mid也就是4

接下来试着解释一下原因(部分内容参考了别人的)
原因: (l+r)/2是向下取整的,但是若区间只有两个元素的时候,r=l+1的,mid = (l+l+1)/2 = (2l+1)/2 = l了,然后就是l更新为了l,没变,导致上次处理的区间和这次处理的区间相同

好,既然向下取整不行,向上取整试试
这里我好像在csapp那本书上看到过,待会取找找看看,挖坑

分类讨论

  • l+r 是奇数 (l+r)/2向上取整等于(l+r+1)/2向下取整,如 l=4,r=5, (l+r)/2向上取整等于5, (l+r+1)/2乡下取整等于5
  • 那么l+r是偶数呢?
    l = 4,r=4 (l+r)/2向上取整也等于(l+r+1)/2向下取整
    若程序中有l=mid这种情况,mid的计算需用(l+r+1)/2计算
    但是右边界也有可能更新为mid啊
    若mid = (l+r+1)/2的时候,区间内只有两个元素,
    l = r-1, mid = (l+r+1)/2=(r-1+r+1)/2=r,r更新为r,又废了,所以不能用这个

就两句话

  1. mid = (l+r)/2的时候,若出现了l = mid,程序就废了,将mid用(l+r+1)/2来计算
  2. mid = (l+r+1)/2的时候,若出现了r = mid,程序就废了,将mid用(l+r)/2来计算

完整代码

public int[] searchRange(int[] nums, int target) {
	int l =0,r=nums.length-1;
	while(l<r){
		int mid = l+r>>1;
		if(nums[mid]>=target)
			r=mid;
		else
			l=mid+1;
	}
	if(nums[l]!=target)
		return new int[]{-1,-1};
	int[] res =new int[2];
	res[0]=l;
	l=0;
	r=nums.length-1;
	while(l<r){
		int mid = (l+r+1)/2;
		if(nums[mid]<=target)
			l= mid; // target最后一次出现的位置在mid(含)的后面,调整左边界
		else
			r= mid-1; // target最后一次出现的位置在mid的前面,调整右边界
	}
	result[1]=l;
	return result;
}

你可能感兴趣的:(算法,学习)