二分简介:在一个有序的数组中,如果我们要查询一个数在数组中的下标,我们会对数组进行遍历,进行暴力的搜索,时间复杂度为O(n),我们可以使用二分法,时间复杂度为O(log n)。进行二分的前提是数组具有单调性。
暴力程序:
int a[10] = {1,2,3,4,5,6,7,8,9};
int ex_search(int x)
{
for(int i=0;i<=9;i++){
if(a[i]==x) return i;
}
return -1;
}
下面是一个普通的二分查找函数:
int binarySearch(const std::vector<int>& arr, int target) {
int left = 0,right = arr.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
同样你可以使用 binarySearch 函数。
#include <bits/stdc++.h>
using namespace std;
int main() {
std::vector<int> arr = {1, 3, 5, 7, 9, 11};
int target = 7;
int result = binarySearch(arr, target);
if (result != -1) cout << "Found at index: " << result << endl;
else cout << "Not found" << endl;
return 0;
}
damn是这个函数太低级了,我们如果想要知道大于等于X的第一个数,或者小于等于X的最后一个数,就无法实现了。
下面是两个模板:
#include <bits/stdc++.h>
using namespace std;
int a[11] = {0,1,2,3,4,4,5,6,7,8,9};
int ex_search(int x)
{
for(int i=0;i<=9;i++){
if(a[i]==x) return i;
}
return -1;
}
int Bsearch_left(int l,int r,int x)
{
while(l<r){
int mid = l+r>>1;
if(a[mid]>=x) r = mid;
else l = mid+1;
}
return r;
}
int Bsearch_right(int l,int r,int x)
{
while(l<r){
int mid = l+r+1>>1;
if(a[mid]<=x) l = mid;
else r = mid-1;
}
return r;
}
int main()
{
cout << Bsearch_left(1,10,4) << '\n';
cout << Bsearch_right(1,10,4);
return 0;
}
分别输出:4 5,对应第一个4和第二个4.
相信作为大佬的你意识到这既是upper_bound和lower_bound两个函数
我们既可以改动代码
#include <bits/stdc++.h>
using namespace std;
int a[11] = {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9};
int ex_search(int x) {
for (int i = 0; i <= 9; i++) {
if (a[i] == x) return i;
}
return -1;
}
int main() {
int l = 1, r = 10, x = 4;
// 使用 lower_bound 替代 Bsearch_left
auto it_left = lower_bound(a + l, a + r + 1, x);
int left_index = (it_left != a + r + 1) ? (it_left - a) : -1;
cout << left_index << '\n';
// 使用 upper_bound 替代 Bsearch_right
auto it_right = upper_bound(a + l, a + r + 1, x);
int right_index = (it_right != a + l) ? (it_right - a - 1) : -1;
cout << right_index << '\n';
return 0;
}
下面有两道简单的例题:
介于我们的函数无论是否查找到,都会返回一个数值,我们需进行检查。
下面是题1的解:
#include <bits/stdc++.h>
using namespace std;
const int N =1e6+5;
int a[N];
int n,m;
int bsearch(int x)
{
int l=1,r=n;
while(l<r){
int mid = l+((r-l)>>1);
if(a[mid]>=x) r = mid;
else l = mid + 1;
}
return r;
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i];
while(m--){
int t;
cin >> t;
int f = bsearch(t);
if(a[f]==t) cout << f << ' ';
else cout << -1 << ' ';
}
return 0;
}
下面是题2的解法1(不使用二分):
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5+5;
map<ll,ll> m;
long long k[N];
int n,c,tot = 0;
ll ans = 0;
int main()
{
cin >> n >> c;
for(int i=1;i<=n;i++){
ll t;
cin >> t;
if(m[t]==0) k[++tot] = t;
m[t]++;
}
sort(k+1,k+tot+1);//可以不加
for(int i=1;i<=tot;i++){
ll a = k[i]+c;
ans+= m[a]*m[k[i]];
}
cout << ans;
return 0;
}
主要的优化就在于使用桶的计数,解决了重复匹配的问题
解法二(使用二分):
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5+5;
long long k[N];
int n;
ll c,ans = 0;
int bsearch_left(int x)
{
int l =1,r = n;
while(l<r){
int mid = l+r>>1;
if(k[mid]>=x) r = mid;
else l = mid+1;
}
return r;
}
int bsearch_right(int x)
{
int l = 1,r = n;
while(l<r){
int mid = l+r+1>>1;
if(k[mid]<=x) l = mid;
else r = mid-1;
}
return r;
}
int main()
{
cin >> n >> c;
for(int i=1;i<=n;i++) cin >> k[i];
sort(k+1,k+n+1);
for(int i=1;i<=n;i++){
ll a = k[i]+c;
if(k[bsearch_left(a)] == a && k[bsearch_right(a)] == a){
ans += (bsearch_right(a)-bsearch_left(a)+1);
}
}
cout << ans;
return 0;
}
可以看一下大佬的博客: