遞增整數序列的二分詳解


二分搜索是一種時間復雜為log2n的算法,可以用於單調函數求根和單調序列查詢的有效算法,即使數列長度高達10^9 也只需二分31次,查詢速度接近常數,同時二分思想是一種很基礎很重要的思想希望同學們都能掌握;

單調數列(單調遞增)通用二分模板

簡潔版

 

完全版

start_index =0end_index=len-1時,功能和簡潔版一樣;

 

1.

 

 

為啥不能寫下面這句

 

ifmid==n

 

return mid

 

因為有時候會有

 

這樣的一種數列

 

1 2 2 2 2 3

 

這個是非嚴格單調序列,如果叫你尋找2第一次出現的位置顯然會出現錯誤

 

所以上面那個寫法是不行的;

 

2.

 

為啥不寫left=mid+1right=mid-1

 

因為有些情況下會不能用

 

比如在數列

 

a[]={0 1 3 4,5}

 

找大於2的第一個位置

 

當你right=4left=0;

 

mid=2A[mid]>2 所以 若r=mid-1=1你就錯過去了,除非你用if判斷一下,但那樣就太冗長難看了;

 

3.

 

為啥不能寫

 

while(left=right)

 

因為上面的原因我們沒用 left=mid+1right=mid-1

 

而是left=mid,right=mid

 

a[]={0,1,3,4};

 

則找2的位置時會死循環

 

因為 left=1right=2

 

mid=(left+right)/2=1

 

a[mid]<2;

 

所以left=mid=1

 

結果left還是等於1right還是等於2

 

永遠死循環

 

現在來解釋一下上面代碼的原理:

我們讓left代表小於等於 x的一方

       right代表大於x的一方

我們每次判斷mid是屬於left還是屬於right來使區間縮小一半;

leftright相遇時,即left+1=right時 分界線就在leftright中間

則從left開始往前小於等於x,right開始往后大於x

同理其實我們也可以讓

iff(mid)<x

left=mid;

else

right=mid;

這時

left代表小於x的一方

         right代表大於等於x的一方

leftright相遇時;

則從left開始往前小於x,right開始往后大於等於x

即如圖所示

 

最后我們來教點小技巧

如果我把前一種記做find_upper_bound()

 

如果我把后一種記做find_lower_bound()

顯然圖觀察可得 兩種的二分結果分別是

x的下標上界和下標下界(包含x

他們相減就是x的個數

顯然若find_upper_bound(x)==find_lower_bound(x);那么就不存在x

在使用時不用寫兩種二分,其實由觀察圖可得

find_upper_bound(x-1)=find_lower_bound(x);

或者find_upper_bound(x)=find_lower_bound(x+1);

所以其實寫一種就行了;

這里給出對照表(前提是整數遞增序列才成立,如果是浮點數或則遞減序列時都不成立)

查詢目標            find_upper_bound的用法    find_lower_bound的用法

大於x的第一個位置        find_upper_bound(x)     find_lower_bound(x+1)

x出現的第一個位置        find_upper_bound(x-1)    find_lower_bound(x)

小於x的第一個位置        find_upper_bound(x-1)-1   find_lower_bound(x)-1

x出現的最后位置         find_upper_bound(x)-1    find_lower_bound(x+1)-1

習題

1.http://210.34.193.66:8080/vj/Problem.jsp?pid=2145

2.http://210.34.193.66:8080/vj/Problem.jsp?pid=1923(這題注意要先用快速排序再二分)

這里是習題1的標程

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM