二分搜索,呵呵

二分搜索 呵呵


寫在前面的話:

二分搜索原理看似都懂,可是真的讓我敲起來,我還真不敢保證寫的必定沒有bug,甚至陷入bug裏不知道怎麼改呢.
其實,二分法真的不是那麼簡單,尤爲是二分的各個變種.下面一次介紹各類變種二分法,而且加上個人總結吧.數組

1. 基本二分法

在一個排好序的數組裏查找一個key值.code

int search(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = left + ((right - left) << 1);
        if (arr[mid] == key) return mid; 
        else if(arr[mid] > key) right = mid - 1;
        else left = mid + 1;
    }
    return -1;
}

若是條件變化成, 數組中的數據可能重複,要求範圍等於key值最小的下標,或者找出數組中第一個大於(或等於)key值的元素下標,請看下面ast

2. 找出第一個與key相等的元素

int searchFirstEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid - 1;
        else if(arr[mid] < key) left = mid + 1;
    }
    if( left < n && arr[left] == key) return left;
    return -1;
}

3. 找出最後一個與key相等的元素

int searchLastEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] > key) right = mid - 1;
        else if(arr[mid] <= key) left = mid + 1; 
    }
    if( right>=0 && arr[right] == key) return right;
    return -1;
}

4. 找出第一個大於或等於key的元素

int searchFirstEqualOrLarger(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid-1;
        else if (arr[mid] < key) left = mid+1;
    }
    return left;
}

5. 找出第一個大於key的元素

int searchFirstLarger(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] > key) right = mid-1;
        else if (arr[mid] <= key) left = mid+1;
    }
    return left;
}

6. 找出最後一個小於或等於key的元素

int searchLastEqualOrSmaller(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int m = (left+right)/2;
        if(arr[m] > key) right = m-1;
        else if (arr[m] <= key) left = m+1;
    }
    return right;
}

7. 找出最後一個小於key的元素

int searchLastSmaller(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid-1;
        else if (arr[mid] < key) left = mid+1;
    }
    return right;
}

個人總結:

  1. 各類二分有一些代碼部分是不變的,記住就好.也很容易記住.
int search(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] /****/ key) right = mid-1;
        else if (arr[mid] /****/ key) left = mid+1;
    }
    return /****/;
}

其中除了3處/****/以外,其餘部分能夠說是模板,很簡短,用的時候敲出來就好了模板

  1. 返回值問題:
    記住left和right是key值存在的範圍. 因此當要找最後一個等於key時,必然返回right . 當要找最大的小魚key值時, 必然返回left
  2. a[mid] == key 時, 究竟是改變left仍是right?
    這裏要用 "不會更壞的"想法來理解比較好.
    好比,當咱們知道要 返回值 和 條件是否包括等於 後,咱們再來看等號如何處理.
    好比:條件包括等號,返回值爲left . 那麼就意味着不取等號那種狀況後所作的操做不能夠將left變化成mid+1,由於一旦這樣變化,最終返回left的時候再也不多是mid,而mid是符合條件(包括等於)要求的 . 這確定是不合理的.因此等號毫不是加在這裏 .

說到這裏,咱們的返回值和等號加在哪裏,2個問題都解決了,就能夠寫完美的二分法啦!
原理

相關文章
相關標籤/搜索