二分法找元素第一次出現位置

常見的二分法找一個元素出現的位置(隨機的)是:
//x[0...n-1]是升序排列。找到元素t出現的一個位置(不一定是第一次出現的位置)
l=0;u=n-1;
loop
    if l>u
        p = -1; break;
    m = (l+u)/2;
    case
        x[m] < t: l = m+1
        x[m] ==t: p=m; break
        x[m] > t: u=m-1



現在需要用二分法找到t第一次出現的位置。《編程珠璣》介紹了一個程序寫法,很有意思。
如果第一個t的位置是m,x[m]=t, 則x[0...m-1]<t<=x[m...n-1]。
找到m滿足這樣的關係。用二分法的思想去找到這樣的位置。二分法有兩個變量l,u來表示數組的兩端。找到m就相當於找到這樣條件的l和u:
l+1=u,x[l]<t<=x[u]
我們需要從數組兩端開始循環尋找滿足條件的l,u。第四章介紹的驗證循環正確性的知識:初始化時確立不變式,然後每次迭代都保存其真值,最後有終止的條件。
如果數組元素個數多於1,初始化l=0,u=n-1,尋找的循環程序就有不變量:x[l]<t && x[u]>=t && l<u。但是有特殊情況,
1. x[0]=t,如果初始化l=0,u=n-1,初始化時不變量就不能滿足。
2. 只有一個元素l=0,u=0,初始化時不變量也不能滿足。
如果直接這樣寫程序就必須處理特殊情況,程序不簡練。
書中程序用數組兩端兩個剛越出數組範圍的值(-1和n)來初始化l和u,並假想x[-1]<t,x[n]>=t,而程序不會訪問這兩個變量。這確實有想象力,突破一種思維限制:用數組範圍內值來初始化變量。這樣各種情況下初始化時不變量(x[l]<t && x[u]>=t && l<u)都能被滿足。我估計初始有這種想法的牛人應該也是想到這個不變量(x[l]<t && x[u]>=t && l<u),然後發現特殊情況不是都滿足,然後有了這樣的巧妙想法。用這種方法寫出的程序很簡練。當然我們也可以用常規的二分法找到t的某個位置,然後往前找到第一個。

l=-1;u=n;
while(l+1 != u)
    m = (l+u)/2;
    if x[m] < t
        l = m
    else
        u = m
p = u;
if (p>=n || x[p]!=t)
    p = -1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章