關於二分法的探索

二分法一直被認爲是最「簡單」的排序方法之一,它充分地利用了數組遞增性質,許多二分法的變形問題也很是經典。數組

1.二分

二分法最簡單的實現以下:spa

/** * binary search, assure that all inputs are valid! */
int binary_search(int array[], int left, int right, int target) { int mid = 0; while(left <= right) { mid = (left+right)/2; if(array[mid] == target) return mid; else if(array[mid] > x) right = mid-1; else left = mid+1; } return -1; }

 

值得注意的是,若二分上界超過int型數據範圍的一半,那麼若是查詢元素在序列較靠後的位置時,語句:code

mid = (left+right)/2;

中的和有可能超出int的範圍而致使溢出。更合理的寫法則是:blog

mid = left+(right-left)/2;

注意,right的初值必定是數組長度-1,不然有可能越界,其實際含義就是尋找的範圍不能超過數組的範圍。以上的代碼能夠處理有多個相同元素的遞增數組(非嚴格遞增),但不能肯定找到的目標值是多個相同元素的第幾個,因此引出了另外一個問題:咱們能不能在有重複元素的遞增數組中找到第一個大於等於目標值的位置?排序

2.變形一:在有重複元素的遞增數組中找到第一個大於等於目標值的元素的位置

分析一下問題:找到第一個大於等於目標值的元素的位置,並不必定保證該位置必定在數組範圍內,因此此處right的初值爲數組長度(即數組最大索引+1),代表該位置有可能取到的最大值。簡單的實現以下:索引

/** * lower_bound version of binary search, assure that all inputs are valid! */
int lower_bound(int array[], int left, int right, int target) { int mid = 0; while(left < right) { mid = (left+right)/2; if(array[mid] >= target) right = mid; else left = mid+1; } return left; }

對比binary_search和lower_bound,能夠發現有三處不一樣:get

①left < rightinput

爲何不加上相等的狀況?由於相等狀況代表已經找到該位置。io

②if(array[mid] >= target) right = mid;class

爲何合併相等和大於的狀況,而且right = mid而不是mid-1?由於相等或大於目標值代表,mid所在的位置有多是所找位置,也有可能在該位置的左邊,所以咱們應該把右邊界置於此,而不該該減少,由於減少就有可能錯過該位置。

③else left = mid+1;

爲何left = mid+1而不是mid?由於處於array[mid] < target的條件下,mid不多是所尋找的位置,須要增大左邊界,在新的範圍內繼續尋找。

3.變形二:在有重複元素的遞增數組中找到第一個大於目標值的元素的位置

分析一下問題:找到第一個大於目標值的元素的位置,只在變形一的基礎上作了一點改變,在代碼中應該體如今尋找過程當中邊界上的改變:

/** * upper_bound version of binary search, assure that all inputs are valid! */
int upper_bound(int array[], int left, int right, int target) { int mid = 0; while(left < right) { mid = (left+right)/2; if(array[mid] > target) right = mid; else left = mid+1; } return left; }

對比lower_bound和upper_bound,能夠發現有一處不一樣:

①if(array[mid] > target) right = mid;

若是理解了lower_bound的思想,那這個也很簡單了,將大於等於改成大於,實際上就是等於這個條件下,mid不多是尋找的位置,所以須要增大,因此放在else中,經過增大左邊界來實現。

4.實戰:木棒切割問題

問題:給出N根木棒,長度已知,如今但願經過切割它們來獲得至少K段長度相等的木棒,求這些長度相等的木棒最長能有多長?

分析:首先能夠肯定的是,切割獲得的木棒長度越長,K越小,隱含一個遞增序列的條件。而每段木棒的長度最大爲N根木棒的最大長度,最小爲1,每一個長度對應一個切割的段數,構成一個遞增的序列。因此將問題轉換爲在該序列中,求出第一個大於等於K的位置,該位置對應的長度即爲所求長度,利用變形一便可求解:

int cut_woods(int array[], int m, int k) { int max = 0; // first, find the wood having maximum length
    for(int i = 0; i < m; ++i) { if(array[i] > max) max = array[i]; } // second, binary search between 0 and max length
    int left = 0, right = max-1, mid = 0; while(left < right) { mid = (left+right)/2; int counts = 0; for(int i = 0; i < m; ++i) { counts += array[i]/(max-mid); } if(counts >= k) right = mid; else left = mid+1; } return max-left; }

須要注意的是,每段長度和段數是反比關係,因此最終的每段長度須要用最大長度max_length減去所得位置獲得。

相關文章
相關標籤/搜索