二分查找

二分查找

一、基本原理

二分查找又名折半查找,是一種效率較高的查找方法,時間複雜度是O(logn),要求待查找序列必須有序。

二、傳統代碼模板

public int binarySearch(int[] nums, int target) {
    int low = 0;
    int high = nums.length - 1;
    while (low <= high){
        int mid = (low + high) / 2;
        if (nums[mid] == target){
            return mid;
        }else if (nums[mid] < target){
            low = mid + 1;
        }else {
            high = mid - 1;
        }
    }
    return low;
}

存在的問題如下:

1、當待查找序列的長度非常大的時候,取mid時可能出現整型溢出。

2、退出while循環後,需要考慮到底是返回low還是high。

三、改進版

1、中位數取法

int mid = low + (high - low) / 2;

或者

int mid = (low + high) >>> 1;

2、while循環條件修改爲low<high,當退出循環的時候,low==high成立,此時返回值就可以任選其一。

public int binarySearch(int[] nums, int target) {
    int low = 0;
    int high = nums.length;
    int mid = 0;
    while (low < high){
        mid = low + (high - low) / 2;
        if (judge()){
            low = mid + 1;
        }else {
            high = mid;
        }
    }
    return low;
}

public int searchInsert(int[] nums, int target) {
    int low = 0;
    int high = nums.length;
    int mid = 0;
    while (low < high){
        mid = low + (high - low + 1) / 2;
        if (judge()){
            high = mid - 1;
        }else {
            low = mid;
        }
    }
    return low;
}

四、例題

1、LeetCode35 搜索插入位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        int low = 0;
        int high = nums.length;
        int mid = 0;
        while (low < high){
            mid = low + (high - low) / 2;
            //當nums[mid]<target時直接更新下邊界,因爲此時的mid一定不是插入位置
            //但是當nums[mid]>target時,卻不能直接更新上邊界high=mid-1,因爲mid可能爲插入位置
            if (nums[mid] < target){
                low = mid + 1;
            }else {
                high = mid;
            }
        }
        return low;
    }
}

2、LeetCode69 x的平方根

class Solution {
    public int mySqrt(int x) {
        if (x == 0 || x == 1){
            return x;
        }
        int low = 0;
        int high = x / 2;
        while (low < high){
            int mid = low + (high - low + 1) / 2;
            //根據題意,最後結果是向下取整,所以當mid>x/mid時,直接更新上邊界,因爲mid的平方不可能比x大
            //但是當mid<x/mid,不能直接更新下邊界low=mid+1,因爲有可能mid就是最後要求的值。
            if (mid > x / mid){
                high = mid - 1;
            }else {
                low = mid;
            }
        }
        return low;
    }
}

3、LeetCode162 尋找峯值

class Solution {
    public int findPeakElement(int[] nums) {
         if (nums.length == 1){
            return 0;
        }
        if (nums.length == 2){
            if (nums[0] > nums[1]){
                return 0;
            }else {
                return 1;
            }
        }
        int start = -1;
        int end = nums.length;
        while (start < end){
            int mid = start + (end - start) / 2;
            int left = mid - 1 < 0 ? Integer.MIN_VALUE : nums[mid - 1];
            int right = mid + 1 == nums.length ? Integer.MIN_VALUE : nums[mid + 1];
            //如果nums[mid]的值大於左右值,那麼mid即爲峯值位置
            //如果nums[mid]的值比左邊大,比右邊小,說明nums[mid]處於一個上升的序列當中,那麼需要更新下邊界,繼續尋找峯值,但是不能直接start=mid+1,因爲此時只能確定[start,mid-1]區間是要捨棄的區間,需要去[mid,end]區間中尋找峯值。更新上邊界的原理與此相似。
            if (nums[mid] > left && nums[mid] > right){
                return mid;
            }else if (nums[mid] > left && nums[mid] < right){
                start = mid;
            }else {
                end = mid;
            }
        }
        return 0;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章