1. 程式人生 > >二分搜尋法學習

二分搜尋法學習

文章參考:https://blog.csdn.net/yusiguyuan/article/details/48052659
https://www.cnblogs.com/grandyang/p/6854825.html

一、二分搜尋法

在一個有序的陣列中,查詢一個數,可以使用二分查詢。
比如程式碼:

//二分搜尋法
int binary_search(int* array, int n, int key)
{
    int low = 0;
    int high = n - 1;
    int mid;
    if (high < low)
        return
-1; while (high >= low) { mid = low + ((high - low) >> 1); if (array[mid] == key) return mid; else if (array[mid] > key) high = mid - 1; else low = mid + 1; } return -1; }

但是對於上面的二分查詢還有另外一種寫法,這是寫作習慣的問題:

如果high = n-1 => while(high>=low) => high = middle-1;

如果high = n  => while(high > low)  => high = middle;

二、二分搜尋的變形

1)lower_bound 查詢第一個不小於目標值的數
//lower_bound 查詢第一個不小於目標值的數
int lower_bound(int* array, int low, int high, int key)
{
    int mid;
    if (high< low)
        return
-1; while (high >= low) { mid = low + ((high - low) >> 1); if (array[mid] < key) low = mid + 1; else high = mid - 1; //cout<<mid<<endl; } return low; }

比如在陣列[2, 4, 5, 6, 9]中查詢數字3,就會返回數字4的位置;在陣列[0, 1, 1, 1, 1]中查詢數字1,就會返回第一個數字1的位置。

2)查詢第一個大於目標值的數
//查詢第一個大於目標值的數
int upper_bound(int* array, int low, int high, int key)
{
    int mid;
    if (high < low)
        return -1;
    while (high >= low)
    {
        mid = low + ((high - low) >> 1);
        if (array[mid] > key)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return low;
}

比如在陣列[2, 4, 5, 6, 9]中查詢數字3,還是返回數字4的位置,因為數字4在此陣列中既是第一個不小於目標值3的數,也是第一個大於目標值3的數,所以make sense;在陣列[0, 1, 1, 1, 1]中查詢數字1,就會返回座標5,通過對比返回的座標和陣列的長度,我們就知道是否存在這樣一個大於目標值的數。

三、標準STL中lower_bound和upper_bound

在從小到大的排序陣列中:

lower_bound( begin,end,num):從陣列的begin位置到end-1位置二分查詢第一個大於或等於num的數字,找到返回該數字的地址,不存在則返回end。通過返回的地址減去起始地址begin,得到找到數字在陣列中的下標。

upper_bound( begin,end,num):從陣列的begin位置到end-1位置二分查詢第一個**大於**num的數字,找到返回該數字的地址,不存在則返回end。通過返回的地址減去起始地址begin,得到找到數字在陣列中的下標。


#include <iostream>
#include <algorithm>//必須包含的標頭檔案
using namespace std;

int main()
{
    int arr[] = { 2, 3, 3, 6, 9 };
    //第一個大於或等於num數字的位置
    cout << lower_bound(arr, arr + 5, 3) - arr << endl;  //1
    //第一個大於num數字的位置
    cout << upper_bound(arr, arr + 5, 3) - arr << endl;  //3
    getchar();
    return 0;
}

四、二分查詢的遞迴非遞迴實現

以下整理了網上經典的二分查詢的遞迴與非遞迴方法。


#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;

//[email protected] July
//隨時歡迎讀者找bug,email:[email protected]

//首先要把握下面幾個要點:
//right=n-1 => while(left <= right) => right=middle-1;
//right=n   => while(left <  right) => right=middle;
//middle的計算不能寫在while迴圈外,否則無法得到更新。

int binary_searchIteration(int array[], int n, int value)
{
    int left = 0;
    int right = n - 1;

    while (left <= right)          //迴圈條件,適時而變
    {
        int middle = left + ((right - left) >> 1);  
        //防止溢位,移位也更高效。同時,每次迴圈都需要更新。

        if (array[middle]>value)
        {
            right = middle - 1;   //right賦值,適時而變
        }
        else if (array[middle]<value)
        {
            left = middle + 1;
        }
        else
            return middle;
    }
    return -1;
}


int binary_searchRecursion(int arr[], int low, int high, int key) {
    if (low > high) return -1;

    int mid = (high + low) / 2;   //中間元素,防止溢位
    if (key == arr[mid])return mid; //找到時返回
    else if (key > arr[mid]) {
        return binary_searchRecursion(arr, mid + 1, high, key); 
        // 在更高的區間搜尋
    }
    else {
        return binary_searchRecursion(arr, low, mid - 1, key); 
        // 在更低的區間搜尋
    }
}


int main()
{
    int arr[] = { 2, 3, 3, 6, 9 };
    cout << binary_searchIteration(arr,5, 6) << endl;
    cout << binary_searchRecursion(arr, 0,5, 2) << endl;

    getchar();
    return 0;
}