通俗易懂的二分查找及其變體問題

簡單的二分查找

很容易理解,可是二分查找的前提是序列是有序的。每次都是經過跟區間的中間元素對比,將待查找的區間縮小爲以前的一半,直到找到要查找的元素,或者區間被縮小爲0。
二分查找只能用在數據是經過順序表結構存儲的數據結構上,如數組,若是是其餘的結構存儲則不適合用二分查找,如鏈表。
二分查找的時間複雜度爲O(logn).
簡答二分查找的代碼以下:算法

int binarySearch(int A[], int n, int x){
        
        //left 和right爲數組的左右區間下標   
        int left = 0, right =n-1, mid;
        while(left <= right){
                mid = left + ( right - left) / 2; //當數組數量較多時爲了防止left+right超過int範圍。 
                if(A[mid] == x) return mid; //找到x,返回下標
                else if(A[mid] > x){
                        right = mid-1;
                } else{
                        left = mid+1;
                }
        } 
        return -1; //沒有找到,返回-1。 
}

二分查找變體:

若是序列A中的元素可能重複,因此查找時會出現如下變體類型的二分查找。該思想是在學習極客時間的王錚數據結構與算法時學習的,對我來講比不少書上寫得更加容易理解和記憶。數組

查找序列中第一個等於x的元素的位置

由於數組中有重複值,找到的x不必定是第一個出現的,因此能夠在上面簡單二分查找的基礎上判斷當A[mid]=x 時,判斷是否是第一個,若是此時mid=0或者A[mid-1]!=x,則說明x確定爲第一個出現;若A[mid-1]=x,則要更新right = mid-1,由於此時第一個x確定在[left, mid-1]之間。代碼以下:數據結構

//查找第一個等於x的位置 
int findfirstX(int A[], int n, int x){
        int left = 0, right = n-1;
        int mid;
        while(left <= right){
                mid = left + ((right-left) >> 1);
                if(A[mid] > x){
                        right = mid-1;
                }else if(A[mid] < x){
                        left = mid + 1;
                }else{
                        if(mid == 0 || A[mid-1] != x) return mid;
                        else right = mid-1;
                }
        }
        return -1;
}

查找序列中最後一個等於給定值的元素

該問題和上面一樣的思路,要判斷mid是否爲n-1,或者A[mid+1]即後一個是否爲x,而後進行更新返回。學習

//查找最後一個等於x的元素位置
int findlastX(int A[], int n, int x){
        int left = 0, right = n-1, mid;
        while(left <= right){
                mid = left + ((right-left) >> 1);
                if(A[mid] > x){
                        right = mid-1;
                }else if(A[mid] < x){
                        left = mid + 1;
                }else{
                        if(mid == n-1 || A[mid+1] != x) return mid;
                        else left = mid+1;
                }
        }
        return -1;
}

查找序列中第一個大於等於x的元素的位置

思路和前面兩種相似,若是A[mid]小於要查找的值value,那要查找的值確定在[mid+1, high]之間,若是A[mid] 大於等於給定的值x,要先看下這個mid=0 || A[mid-1] < x,則此時確定時第一個大於等於x的值。不然A[mid-1]>x,則要找的值確定在[left, mid-1]之間,因此更新right = mid-1;code

//查找第一個大於等於x的元素位置
int Upperfind(int A[], int n, int x){
        int left = 0, right = n-1, mid;
        while(left <= right){
                mid = left + ((right-left) >> 1);
                if(A[mid] >= x){
                        if((mid==0) || (A[mid-1] < x)) return mid;
                        else right = mid-1;
                }else{
                        left = mid+1;
                }
        } 
}

查找序列中最後一個小於等於x的元素的位置

思路同上面同樣。代碼以下:get

//查找最後一個小於等於x的元素位置
int boundfind(int A[], int n, int x){
        int left =0, right = n-1, mid;
        while(left <= right){
                mid = left + ((right- left) >> 1);
                if(A[mid] <= x){
                        if((mid==n-1) || (A[mid+1] > x)) return mid;
                        else left = mid+1;
                }else{
                        right = mid-1;
                }
        }
}

查找序列中第一個大於x的元素的位置

//查找第一個大於x的元素位置
int findlargeX(int A[], int n, int x){
    int left =0 ,right = n-1, mid;
    while(left <= right){
        mid = left + ((right- left) >> 1);
        if(A[mid] > x){
            if((mid==0) || (A[mid-1] < x)) return mid;
            else right = mid - 1;
        }else{
            left = mid + 1;
        }
    }
    return -1;
}

參考資料:
極客時間 王崢算法課程:https://time.geekbang.org/column/article/42520
算法筆記 胡凡 曾磊ast

相關文章
相關標籤/搜索