二分法总结 (以后直接就是抄板子了!)

这两天又碰到了用到了二分法的题,但是由于我之前学的不是很清楚,许多二分的题都是凭感觉写出二分格式,然后WA之后凭感觉调整(竟然还能A过!!),因此再次碰到之后就感觉还是需要总结一下,要不然以后太浪费时间了。

我们用套路的方法来二分:

对于一个区间[l,r],满足 循环不变式: 
 arr[l]<=key    arr[r]>=key
对于区间[l,r]里面存在元素则表示还在区间内,根据不同的状态,
判断 arr[mid]==key 时的区间缩减状态 

1.对于查找目标值的二分查找


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中,
arr[mid]==key时返回mid即可 
若最后区间[l,r]中没有元素,则说明key不再数组中,返回-1即可 

int search(int *arr,int n,int key)
{//在一个排好序的数组中找到key 
	int l=0,r=n-1,mid;
	while(l<=r)//结束时,[l,r]数组为空 
	{
		mid=(l+r)>>1;
		if(arr[mid]==key)
			return mid;
		else if(arr[mid]>key)//key只会在 l~mid 区间,且mid不会是key,因此为 l~mid-1 
			r=mid-1;
		else
			l=mid+1;//key只会在 mid~r 区间,且mid不会是key,因此为 mid+1~r 
	}
	return -1;//没有找到 
}

例题:HDU-3763-CD  题解:https://blog.csdn.net/qq_40482358/article/details/81711039

HDU-2141-Can you find it?   题解:https://blog.csdn.net/qq_40482358/article/details/84716100

2.查找第一个与key相等的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
arr[mid]==key时:
因为要查找第一个与key相等的元素,因此此时mid可能不是第一个与key相等的元素,因此区间缩小[l,mid-1]
当区间中没有元素的时候,此时必定 r<l ,
当l==r的时候,如果此时arr[mid]==key,l不变,r-1,答案为l。 
如果最后一次 l!=r ,则r=l+1,
若arr[mid]==key,mid==l所以结果是l 
若arr[mid]<key,转成了l==r状态 

int search_first_key(int *arr,int n,int key)
{//找第一个与key相等的元素的位置 
	int l=0,r=n-1,mid;
	while(l<=r)//	[l,r]数组为空 
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//此时key必定在 [l,mid-1] 区间 
			r=mid-1;
		else if(arr[mid]<key)//此时key必定在 [mid+1,r] 区间 
			l=mid+1;
		else//此时key==mid,mid可能是答案区间,因此答案为[l,mid]区间 
			r=mid-1;
	}
	if(l<n&&arr[l]==key)//最后出来的时候,l必定在r的右边 
		return l;
	return -1;
}

3.查找最后一个与key相等的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
arr[mid]==key时:
因为要查找最后一个与key相等的元素,因此此时mid可能不是最后一个与key相等的元素,因此区间缩小[mid+1,r]
当区间中没有元素的时候,此时必定 r<l ,
当l==r的时候,如果此时arr[mid]==key,满足循环不变式:arr[l]<=key&&arr[r]>=key ,r不变,l+1,答案为r。 
如果最后一次 l!=r ,则r=l+1,
若arr[mid]<key,满足循环不变式:arr[l]<=key&&arr[r]>=key,mid==l所以结果是r 
若arr[mid]==key,转成了l==r状态 

int search_last_key(int *arr,int n,int key)
{//找出最后一个与key相等的元素的位置 
	int l=0,r=n-1,mid;
	while(l<=r) 
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//显而易见,key在[l,mid-1]位置 
			r=mid-1;
		else if(arr[mid]<key)//显而易见,key在[mid+1,r]位置 
			l=mid+1;
		else//key在[mid,r]位置  
			l=mid+1;
	}
	if(r>=0&&arr[r]==key)//最后一个,r在l的左边 
		return r;
	return -1;
}

例题:HDU-6383-p1m2,题解:https://blog.csdn.net/qq_40482358/article/details/81709166

POJ-1064-Cable master,题解:https://blog.csdn.net/qq_40482358/article/details/81709228

HDU-hdu-1969-pie,        题解:https://blog.csdn.net/qq_40482358/article/details/79344461

POJ-3258-River Hopscotch  题解:https://blog.csdn.net/qq_40482358/article/details/81712748

HDU-2199-Can you solve this equation?  题解:https://blog.csdn.net/qq_40482358/article/details/84713192


4.查找第一个大于等于key的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
arr[mid]==key时:
因为要查找第一个大于等于key的元素,因此此时mid可能不是第一个与key相等的元素,因此区间缩小[l,mid-1],
如果此时arr[r]<key,那么最后结束的时候必定r一直不变,l在r+1,即mid处 。 
当区间中没有元素的时候,此时必定 r<l ,
当l==r的时候,如果此时arr[mid]==key,满足循环不变式:arr[l]<=key&&arr[r]>=key ,l不变,r-1,答案为l。 
如果最后一次 l!=r ,则r=l+1,
若arr[mid]<key,转成了l==r状态 
若arr[mid]==key,满足循环不变式:arr[l]<=key&&arr[r]>=key,mid==l所以结果是
l

int search_morefirst(int *arr,int n,int key)
{//找第一个大于等于key的元素 
	int l=0,r=n-1,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//显而易见,key在[l,mid-1]区间 
			r=mid-1;
		else if(arr[mid]<key)//		key在[mid+1,r]区间 
			l=mid+1;
		else
			r=mid-1;//				key在[l,mid]区间 
	}
	return l;//						l不变 
}

5.查找第一个大于key的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
按照循环不变式很容易就能推出过程 

int search_more_frist(int *arr,int n,int key)
{//找第一个大于key的元素的位置 
	
	int l=0,r=n-1,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//			key在[l,mid-1]区间 
			r=mid-1;
		else if(arr[mid]<key)//		key在[mid+1,r]区间 
			l=mid+1;
		else
			l=mid+1;//				key在[mid+1,r]区间 
	}
	return l;//
}

例题:HDU-5101-Select        题解:https://blog.csdn.net/qq_40482358/article/details/84637559

6.查找最后一个小于等于key的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
arr[mid]==key时:
因为要查找最后一个小于等于key的元素,因此此时mid可能不是最后一个与key相等的元素,因此区间缩小[mid+1,r],
如果此时arr[l]>key,那么最后结束的时候必定l一直不变,r在l-1,即mid处 。 
当区间中没有元素的时候,此时必定 r<l ,
当l==r的时候,如果此时arr[mid]==key,满足循环不变式:arr[l]<=key&&arr[r]>=key ,r不变,l-1,答案为r。 
如果最后一次 l!=r ,则r=l+1,
若arr[mid]<key, 满足循环不变式:arr[l]<=key&&arr[r]>=key,mid==l所以结果是r
若arr[mid]==key,转成了l==r状态

int search_morelast(int *arr,int n,int key)
{//找最后一个小于等于key元素的位置 
	int l=0,r=n-1,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//			key在[l,mid-1]区间 
			r=mid-1;
		else if(arr[mid]<key)//		key在[mid+1,r]区间 
			l=mid+1;
		else
			l=mid+1;//				key在[mid,r]区间 
	}
	return r;//最后在r处取得最后以一个小于等于key的元素 
}

例题:HDU-5101-Select        题解:https://blog.csdn.net/qq_40482358/article/details/84637559

7.查找最后一个大于key的元素的位置


满足循环不变式:arr[l]<=key&&arr[r]>=key 
key在区间[l,r]中, 
按照循环不变式很容易就能推出过程 

int search_morelast(int *arr,int n,int key)
{//找最后一个小于key元素的位置 
	int l=0,r=n-1,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(arr[mid]>key)//			key在[l,mid-1]区间 
			r=mid-1;
		else if(arr[mid]<key)//		key在[mid+1,r]区间 
			l=mid+1;
		else
			r=mid-1;//				key在[l,mid-1]区间 
	}
	return r;//最后在r处取得最大的小于key的元素 
}

未完待续......

这么系统的总结肯定不是我先写的啊,下面是参考的不知名大佬的博客:

https://blog.csdn.net/ebowtang/article/details/50770315

以后碰到其他的状态的话会继续补充的。

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/84555743