二分搜尋演算法(C++詳解版)

2020-07-16 10:04:38
二分搜尋(Binary Search)是一種比線性搜尋更有效的巧妙演算法。它唯一的要求是陣列中的值是有序的。

二分搜尋演算法測試陣列不是從第一個元素開始,而是從中間的元素開始。如果該元素碰巧包含所需的值,則搜尋結束。否則,中間元素的值要麼大於要麼小於正在搜尋的值。如果它大於期望值,那麼該值(如果它在列表中)將在陣列的前半部分中找到;如果它小於期望值,那麼該值(同樣需要假設它在列表中)將在陣列的後半部分中找到。在任何一種情況下,陣列元素的一半就已經從進一步搜尋範圍中排除。

如果在中間元素中找不到所需的值,那麼對於可能包含該值的一半陣列,將重複該過程。例如,如果要搜尋陣列的後半部分,演算法立即測試其中間元素。如果沒有找到所需的值,那麼搜尋範圍將縮小到該元素之前或之後的陣列的四分之一。這個過程一直持續到被查詢的值被找到,或者沒有更多的元素要測試。

下面是一個函數的虛擬碼,它可以對以升序儲存元素的陣列執行二分搜尋:
Set first to 0
Set last to the last subscript in the array
Set found to false
Set position to -1
While found is not true and first is less than or equal to last
    Set middle to the subscript halfway between first and last
    If array[middle] equals the desired value
        Set found to true
        Set position to middle
    Else If array[middle] is greater than the desired value
        Set last to middle - 1
    Else
        Set first to middle + 1
    End If
End While
Return position
該演算法使用 3 個索引變數:first、last 和 middle。first 和 last 變數標記當前正在搜尋的陣列部分的邊界。它們用陣列的第一個和最後一個元素的下標進行初始化。middle 則是在 first 和 last 元素之間大約中點位置的元素的下標,該值的計算方法就是釆用 first 和 last 相加求和然後除以 2,結果將被儲存在 middle 變數中。

如果沒有精確的中心元素,則使用整除法計算 middle 值,選擇緊鄰中點的元素。如果陣列中間的元素不包含搜尋值,則調整 first 或 last 變數,以便在下一次疊代期間只搜尋陣列的頂部或底部的一半。每次回圈無法搜尋到值時,都會將正在搜尋的陣列部分切割成一半。

以下 C++ 範例程式碼中的 binarySearch 函數將用於在整數陣列上執行二分搜尋。第一個形參 array,其元素的個數為 size,以下語句將搜尋它是否出現了儲存在 value 中的數位。如果找到該數位,則返回其陣列下標。否則,返回 -1,表示該值沒有出現在陣列中。
int binarySearch(const int array[], int size, int value)
{
    int first = 0, //第一個陣列元素
    last = size - 1, //最後一個陣列元素
    middle, //搜尋的中點
    position = -1; //搜尋值的位置
    bool found = false; // 標記
    while (!found && first <= last)
    {
        middle = (first + last) / 2; // 計算中點
        if (array[middle] == value) // 如果在中點發現值
        {
            found = true;
            position = middle;
        }
        else if (array [middle] > value) // 如果值在下半部分
            last = middle - 1;
        else
            first = middle + 1; //如果值在上半部分
    }
    return position;
}
下面的程式是一個使用 binarySearch 函數的完整程式。它將搜尋一個包含員工 ID 號的陣列,以查詢特定的值。
//This program performs a binary search on an integer array whose elements are in ascending order.
#include <iostream>
using namespace std;

//Function prototype

int binarySearch(const int [], int, int);
const int SIZE = 20;
int main()
{
    // Create an array of ID numbers sorted in ascending order
    int IDnums[SIZE] = {101, 142, 147, 189, 199, 207, 222,234, 289, 296, 310, 319, 388, 394, 417, 429, 447, 521, 536, 600 };
    int empID, // Holds the ID to search for
    results;//Holds the search results
    // Get an employee ID to search for
    cout << "Enter the employee ID you wish to search for: ";
    cin >> empID;
    // Search for the ID
    results = binarySearch(IDnums, SIZE, empID);
    // If binarySearch returned -1, the ID was not found
    if (results == -1)
        cout << "That number does not exist in the array.n";
    else
    {
        // Otherwise results contains the subscript of
        // the specified employee ID in the array
        cout << "ID " << empID << " was found in element " << results << " of the array.n";
    }
    return 0;
}

int binarySearch(const int array[], int size, int value)
{
    int first = 0, // First array element
    last = size - 1, // Last array element
    middle,    // Midpoint of search
    position = -1; // Position of search value
    bool found = false; // Flag
    while (!found && first <= last)
    {   
        middle = (first + last) / 2; // Calculate midpoint
        if (array[middle] == value) // If value is found at mid
        {
            found = true;
            position = middle;
        }
        else if (array[middle] > value) // If value is in lower half
            last = middle - 1;
        else
            first = middle +1; // If value is in upper half
    }
    return position;
}
程式輸出結果:

Enter the employee ID you wish to search for: 199
ID 199 was found in element 4 of the array.

二分搜尋的效率

顯然,二分搜尋比線性搜尋更有效率。每次進行比較,找不到所需的專案時,都會剔除必須搜尋的陣列剩餘部分的一半。

例如,考慮有 20 000 個元素的陣列。如果二分搜尋未能在第一次嘗試中找到某個專案,則剩餘要搜尋的元素數量為 10 000。如果在第二次嘗試中未找到該專案,則仍然要搜尋的元素的數量是 5000。這個過程一直持續到二分搜尋找到所需的值或者確定它不在陣列中。如果在陣列中有 20 000 個元素,那麼這最多需要 15 次比較。如果使用線性搜尋的話,則平均需要進行 10 000 次比較,所以二分搜尋的效率高太多了。

可以使用 2 的冪值來計算二分搜尋在任何大小的陣列上進行比較的最大次數。2 的冪值就是以 2 為底數的整數指數冪。只要找出大於陣列中元素個數的 2 的最小冪值,即可知道查詢一個元素所需的最大比較次數,或者確定它不存在。

例如,要在包含 50 000 個元素的陣列中查詢某個特定的專案,則最多只需要進行 16 次比較,因為 216 = 65 536;要在包含 1 000 000 個元素的陣列中查詢某個特定的專案,則最多只需要進行 20 次比較,因為 220= 1 048 576。