二分查詢詳解
阿新 • • 發佈:2021-12-16
將二分查詢問題分為兩種情況討論,一種是有序序列中不存在重複元組,另一種是有序序列中存在重複元素
一、不存在重複元素
分為三種情況討論,>=(大於或者等於)、>(大於)和<=(小於或者等於)
有序序列為{1, 3, 4, 5, 7, 8, 9, 10, 12}
大於或者等於target
可以用lower_bound函式代替
程式碼如下:
#include<bits/stdc++.h> using namespace std; int main(){ vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12}; int n = (int)a.size(); int target; function<int(int)> found = [&](int target){ int left = 0, right = n - 1; while(left < right){ int mid = left + (right - left) / 2; if(a[mid] == target){ return mid; }else if(a[mid] < target){ left = mid + 1; }else{ right = mid; } } return left; }; while(cin >> target){ cout << found(target) << endl; } return 0; }
注意以下幾點:
- 當a[mid]等於target時,直接返回mid;
- 當a[mid]小於target時,left = mid + 1;
- 當a[mid]大於target時,right = mid;
- 迴圈條件是left < right,否則會出現死迴圈的情況。
大於target
可以用upper_bound函式代替
程式碼如下:
#include<bits/stdc++.h> using namespace std; int main(){ vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12}; int n = (int)a.size(); int target; function<int(int)> found = [&](int target){ int left = 0, right = n - 1; while(left < right){ int mid = left + (right - left) / 2; if(a[mid] <= target){ left = mid + 1; }else{ right = mid; } } return left; }; while(cin >> target){ cout << found(target) << endl; } return 0; }
注意以下幾點:
- 當a[mid]小於或者等於target時,left = mid + 1;
- 當a[mid]大於target時,right = mid;
- 迴圈條件是left < right,否則會出現死迴圈的情況。
小於或者等於target
相比於大於target的情況,只需要將返回值改為left - 1即可
程式碼如下:
#include<bits/stdc++.h> using namespace std; int main(){ vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12}; int n = (int)a.size(); int target; function<int(int)> found = [&](int target){ int left = 0, right = n - 1; while(left < right){ int mid = left + (right - left) / 2; if(a[mid] <= target){ left = mid + 1; }else{ right = mid; } } return left - 1; }; while(cin >> target){ cout << found(target) << endl; } return 0; }
二、存在重複元素
分為兩種情況討論,尋找第一個小於或者等於target的位置和第一個大於target的位置
有序序列為{1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12}
第一個大於target的位置
這種情況跟不存在重複元素下對應的情況一致,直接套用上面的程式碼即可
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] <= target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
第一個大於或者等於target的位置
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] == target){
if(mid == left || a[mid - 1] < target){
return mid;
}else{
right = mid;
}
}else if(a[mid] < target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
相比於不存在重複元素時對應的情況,需要對a[mid] == target的情況展開討論,討論情況如下:
- 如果mid恰好是最左邊left位置的元素,那麼直接返回mid即可
- 如果mid不是最左邊left位置的元素,但是mid左邊的第一個元素小於target,即a[mid - 1] < target,那麼也是直接返回mid即可
- 其餘情況下,right = mid。