二分法總結 (以後直接就是抄板子了!)
這兩天又碰到了用到了二分法的題,但是由於我之前學的不是很清楚,許多二分的題都是憑感覺寫出二分格式,然後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
以後碰到其他的狀態的話會繼續補充的。