二分查找(Binary search)

二分查找

概念
二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列。

二分查找有着驚人的查找速度 O(logn)

我們假設數據大小是n,每次查找後數據都會縮小爲原來的一半。最壞情況下,直到查找區間被縮小爲空才停止。
在這裏插入圖片描述
可以看出,這是一個等比數列。其中n/2k=1時,k的值就是總共縮小的次數。而每一次縮小操作只涉及兩個數據大小的比較,所以,經過了k次區間的縮小操作,時間複雜度就是O(k)。我們可以求得k=logn,所以時間複雜度是O(logn)。
舉個例子:我們在42億個數據中超找一個數,用二分查找最多隻需要比較32次(感覺不可思議吧)。

二分查找應用有什麼侷限性呢

首先二分查找依賴的是順序表結構,簡單點說就是數組
那二分查找是否可以依賴於其它的數據結構呢? 比如鏈表。答案肯定是不可以的,主要是二分查找算法要按照下表隨機訪問元素。而鏈表的隨機訪問複雜度是O(n)。所以,如果數據使用鏈表存儲,二分查找的時間複雜度就會很高。

二分查找只能用於在數據是通過順序表來存儲的數據結構上。並且已經是進行過排序的順序結構。如果使用其它數據結構存儲,則無法應用二分查找。

其次,二分查找針對的是有序的數據。
二分查找對這一點要求比較苛刻,數據必須是有序的。如果數據無序,我們首先就是需要進行排序。排序最低的時間複雜度是O(nlogn)。所以,我們針對的是一組靜態的數據,沒有過多的插入刪除操作,我們可以進行一次排序,多次二分查找。這樣排序的成本就會降低。
如果經常需要插入刪除,那我們就要頻繁的排序,這樣維護有序的數據結構成本就非常高。

下面的代碼是實現了一些常見的二分查找的題目。
此篇博客參考於王爭老師的課
代碼和思想是自己理解後的實現。
在這裏插入圖片描述

#include<iostream>
#include<array>
#include<memory>
class search
{
public:
    int s1(int arr[], int len, int ser); //二分查找查找一個數
    int s2(int arr[], int len, int ser); //二分查找 找出第一個值等於給定的元素
    int s3(int arr[], int len, int ser);//查找最後一個值等於給定的元素
    int s4(int arr[], int len, int ser);//查找第一個大於等於給定元素的值
    int s5(int arr[], int len, int ser);//查找最後一個小於等於給定元素的值
};

int search::s1(int arr[], int len, int ser)
{
    int low = 0;
    int high = len - 1;
    int mid;
    while(low <= high)
    {
        mid = low + ((high - low) >> 1); //避免數字溢出int範圍
        if(arr[mid] == ser)
            return mid;
        else if(ser > arr[mid])
            low = mid + 1;
        else if(ser < arr[mid])
            high = mid - 1;        
    }
    return -1;
}

int search::s2(int arr[], int len, int ser)
{
    int low = 0;
    int high = len - 1;
    int mid;
    while(low <= high)
    {
        mid = low + ((high - low) >> 1);
        if(arr[mid] > ser)
            high = mid -1;
        else if(arr[mid] < ser)
            low = mid + 1;
        else
        {
            if(mid == 0 || arr[mid - 1] != ser)
                return mid;
            else 
                high = mid -1;
        }
        
    }
}

int search::s3(int arr[], int len, int ser)
{
    int low = 0;
    int high = len - 1;
    int mid;
    while(low <= high)
    {
        mid = low + ((high - low) >> 1);
        if(arr[mid] > ser)
            high = mid - 1;
        else if(arr[mid] < ser)
            low = mid + 1;
        else
        {
            if(mid == (len -1) || arr[mid + 1] != ser)
                return mid;
            else
                low = mid + 1;
        }
    }
}

int search::s4(int arr[], int len, int ser)
{
    int low = 0;
    int high = len - 1;
    int mid;
    while(low <= high)
    {
        mid = low + ((high - low) >> 1);
        if(arr[mid] < ser)
            low = mid + 1;
        else
        {
            if(mid == 0 || arr[mid - 1] < ser)
                return mid;
            else
            {
                high = mid - 1;   
            }
        }
        
    }
}

int search::s5(int arr[], int len, int ser)
{
    int low = 0; 
    int high = len - 1;
    int mid;
    while(low <= high)
    {
        mid = low + ((high - low ) >> 1);
        if(arr[mid] > ser)
            high = mid -1;
        else 
        {
            if(mid == 0 || arr[mid + 1]  >ser)
                return mid;
            else 
                low = mid + 1;
        }
    }
}
int main()
{
    int arr[15] = {1, 3, 5, 5, 5, 6, 6, 9, 17, 21, 35, 36, 36, 49, 100};
    search solution;
    int result;
    result = solution.s1(arr, 15, 17);
    std::cout << "17 is in arr " << result << "\n";
    result = solution.s2(arr, 15, 5);
    std::cout << "first 5 is in arr " << result << std::endl;
    result = solution.s3(arr, 15, 6);
    std::cout << "last 6 is in arr" << result << std::endl;
    result = solution.s4(arr, 15, 10);
    std::cout << "the first big than 10 is " << result << "\n";
    
    result = solution.s5(arr, 15, 6);
    std::cout << "last small than 6 is " << result  << "\n";
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章