二分查找模版(必背)

模板 1 - binary_search

  • 沒有重複元素時,目標值若存在,則返回索引;若不存在,返回 -1
  • 存在重複元素時,目標值若存在,則返回最小索引;若不存在,返回 -1

模板 2 - lower_bound

  • 返回大於(含等於)目標值的最小索引(第一個大於、等於目標值的索引返回0)

模板 3 - upper_bound

  • 返回大於等於目標值的最大索引+1(第一個大於目標值的索引返回3)

注意點:

- 跳出時都有lo + 1 == hi統一返回lo + 1或hi

- lo = -1 and hi = nums.size()和 lo = 0 and hi = nums.size() - 1取中時一致
/*爲什麼返回 `lo+1`,而不是 `hi`?(退出循環時有 lo + 1 == hi)
    模板開始時將 (lo, hi) 看做是一個開區間,通過不斷二分,最終這個區間中只會含有一個值,即 (lo, hi]
    返回 lo+1 的含義是,結果就在 lo 的下一個;
    在迭代的過程中,hi 會從開區間變爲閉區間,而 lo 始終是開區間,
    返回 lo+1 顯得更加統一。
    當然,這跟迭代的寫法是相關的,你也可以使最終的結果區間是 [lo, hi)nums[mid]<=v,
    這取決於個人習慣。
*/

int binary_search(vector<int> nums, int  n){
    if(nums.size() < 1) return -1;
    int lo = -1, hi = nums.size();
    while(lo + 1 < hi){
        int mid = lo + (hi - lo)/2;
        if(nums[mid] < n)
            lo = mid;
        else
            hi = mid;
    }
  	if(hi == nums.size()) return -1;//lo+1 = hi,防止return時訪問越界
    return nums[lo+1] == n ? lo+1 : -1;
}

//滿足條件<lo往右,else收縮(low,high]終值在閉區間右側上6667(-1,0]
int lowwer_bound(vector<int> nums, int  n){
    if(nums.size() < 1) return -1;
    int lo = -1, hi = nums.size();
    while(lo + 1 < hi){
        int mid = lo + (hi - lo)/2;
        if(nums[mid] < n)
            lo = mid;
        else
            hi = mid;
    }
    return lo + 1;
}

//滿足條件<=lo往右,=繼續往右找到[low,high)6667[2,3)
int upper_bound(vector<int> nums, int  n){
    if(nums.size() < 1) return -1;
    int lo = -1, hi = nums.size();
    while(lo + 1 < hi){
        int mid = lo + (hi - lo)/2;
        if(nums[mid] <= n)
            lo = mid;
        else
            hi = mid;
    }
    return lo + 1;
}

int main(){
    vector<int> test{1,2,2,3,4,6,6,6,8,9};
    auto ret = blowwer_bound(test, 10);
  	//10,下標已經越界,但和下題不同,下題要求找不到返回的是-1
    cout << ret;
}

做到題測試一下,先別看答案。

LeetCode練習:34. Find First and Last Position of Element in Sorted Array

int lowwer_bound(vector<int> nums, int  n){
    if(nums.size() < 1) return -1;
    int lo = -1, hi = nums.size();
    while(lo + 1 < hi){
        int mid = lo + (hi - lo)/2;
        if(nums[mid] < n)
            lo = mid;
        else
            hi = mid;
    }
    if(hi == nums.size()) return -1;
    return nums[hi] == n ? hi : -1;
}
int upper_bound(vector<int> nums, int  n){
    if(nums.size() < 1) return -1;
    int lo = -1, hi = nums.size();
    while(lo + 1 < hi){
        int mid = lo + (hi - lo)/2;
        if(nums[mid] <= n)
            lo = mid;
        else
            hi = mid;
    }
    if(lo == -1) return -1;
    return nums[lo] == n ? lo : -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
    vector<int> res;
    res.push_back(lowwer_bound(nums, target));
    res.push_back(upper_bound(nums, target));
    return res;
}

附上常見的寫法,其實本質是一樣的:

template<typename T>
int binarySearch(vector<T> &vec, T key){
    if(vec.size() < 0)
        return -1;
    int low = 0;
    int high = vec.size() - 1;
    int mid = 0;
    //low=high時不能跳出,此時需要進去進行vec[mid]==key判斷
    while(low <= high){
        mid = low + (high - low)/2;
        if(vec[mid] == key)
            return mid;
        if(vec[mid] < key)
            low = mid + 1;
        else
            high = mid - 1;
    }
    //沒找到返回-1
    return -1;
}

擴展:

/*
二分查找最優解的模板:
    在一定精度下,求滿足條件 C 的最大/小解 x

注意點:
    - 初始化上下界:
        求最大值時,將下界 lo 初始化爲一個特殊的可行解;上界初始化爲一個不滿足條件的解
        求最小值時,反之
    - 一般這種最優解問題會提供精度要求,需要在返回時處理
        - 最大值用“下取整” floor(ret * 1000) / 1000 ——三位精度,floor: 地板
        - 最小值用“上取整” ceil(ret * 1000) / 1000  ——三位精度,ceil:  天花板

*/

/*例 1:
求大於 目標值 v(>0) 的最小值,精確到 3 位小數
*/
double min_bigger(double v) {
    // 初始化上下界
    double lo = v - EPSILON, hi = INF;
    for (size_t i = 0; i < 100; i++) {          // 100 次循環已經能達到相當的精度
        double mid = lo + (hi - lo) / 2.0;
        if (mid <= v)
            lo = mid;
        else
            
            hi = mid;
    }
    // 因爲 hi 在解空間中(hi 必大於 v),所以返回 hi
    return ceil(hi * 1000) / 1000; // 三位精度
}


/*例 2:POJ No.1064
    有 N 條繩子,長度分別爲 Li。如果從它們中切割出 K 條長度相同的繩子,求這 K 條繩子的最大長度。答案保留 2 位小數。
*/
class Solution {
public:
    double max_length(vector<double> ls, int k) {
        double lo = 0, hi = INF;

        for (size_t i = 0; i < 100; i++) {
            double mid = lo + (hi - lo) / 2;

            if (C(ls, mid, k))
                lo = mid;
            else
                hi = mid;
        }
        // 因爲 lo 在解空間中,所以返回 lo
        return floor(lo * 100) / 100;
    }
    
private:
    bool C(vector<double> ls, double mid, double k) {
        int n = 0;
        for (size_t i = 0; i < ls.size(); i++) {
            n += ls[i] / mid;
        }
        return n >= k; 
        // n >= k 說明還有加大的空間,所以當 C 返回 true 時,lo = mid
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章