1. 程式人生 > 其它 >C++ STL中的二分法

C++ STL中的二分法

二分法介紹

狹義的二分法是一種在有序的陣列中查詢是否存在某個值的演算法。廣義的二分法不一定需要顯式的陣列,只需要有序的可能解空間即可,。
有序解空間:設[a,b]是問題P的可能解空間(解必須是整數,a,b也是整數),可能解空間有序等價於若x是問題P的合法解,則任意c小於(大於)x一定是問題的合法解。
不嚴謹地說,由於單調有界可知存在一個分界點,一側是合法解,另一側是不合法解(根據問題需要,定義分界點和某一側重合)。

最流行的二分法是使用兩個指標left和right。該方法用於查詢是否存在某個值較好(狹義),但對於找分界點的問題(廣義),該方法最後必然會得到left=right+1的結果,這時難以弄清楚left和right誰是真正的分界點。
查詢了C++ STL程式碼發現,STL沒有使用這種方法。在STL中,使用一個指標left,指向當前最小的未判斷元素,一個count計數器,表示當前未判斷元素個數。

C++ STL中的程式碼

Algorithm標頭檔案中的std::lower_bound()可以在有序解空間中查詢大於val的最小數下標。

std::lower_bound()程式碼如下:

template <class ForwardIterator, class T>
  ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator it;
  iterator_traits<ForwardIterator>::difference_type count, step;
  count = distance(first,last);
  while (count>0)
  {
    it = first; step=count/2; advance (it,step);
    if (*it<val) {                 // or: if (comp(*it,val)), for version (2)
      first=++it;
      count-=step+1;
    }
    else count=step;
  }
  return first;
}

該方法需要顯式的解空間作為引數,實際情況下並非所有解空間都能被顯式儲存,有時只知道一個函式f(),f(index)表示解空間的第index個值。在這種情況下,需要自己直接實現std::lower_bound()的功能。

程式碼分析

不妨設下標大於(等於)分界點的一側為右半空間,小於(等於)分界點的一側為左半空間。
不論左半空間和右半空間哪個是合法的,std::lower_bound()均可以找到右半空間的最小元素下標。

寫程式碼注意點:

  • while條件count>0,因為count=0表示沒有數是未判斷狀態了,所以迴圈應該結束。
  • if條件應該是it屬於左半空間的充要條件,因為如果it滿足條件直接跳到it+1,說明滿足這個條件的都跳過。結合目標是找右半空間的最小元素下標可知,被跳過的都是左半空間元素。此時,由於跳過[first, first+step],共step+1個數,所以count-=step+1。
  • 如果it不滿足條件,那麼it屬於右半空間,只剩下[first,first+step-1]是未判定的元素,共count個數,所以count=step。