模板 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
}
};