1. 程式人生 > >二分法總結 (以後直接就是抄板子了!)

二分法總結 (以後直接就是抄板子了!)

這兩天又碰到了用到了二分法的題,但是由於我之前學的不是很清楚,許多二分的題都是憑感覺寫出二分格式,然後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

以後碰到其他的狀態的話會繼續補充的。