二分算法笔记
三个模板
基础模板
这个板子是最基础的折半查找,既不偏左也不偏右
int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
return -1;
}
acwing的算法板子
模板一
当我们将区间[l, r]
划分成[l, mid]
和[mid + 1, r]
时,其更新操作是r = mid
或者l = mid + 1;
,计算mid
时不需要加1。
int bsearch_1(int l, int r) { // 这个模板实质从左往右找
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
模板二
当我们将区间[l, r]
划分成[l, mid - 1]
和[mid, r]
时,其更新操作是r = mid - 1
或者l = mid;
,此时为了防止死循环,计算mid
时需要加1。
int bsearch_2(int l, int r) { // 这个模板实质从右往左找
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
使用板子的时候要注意数据范围,计算mid
时可能会爆int,因此推荐默认使用long long
来声明mid
洛谷题单
模板题
因为要输出的是数字第一次出现的位置且数字可能出现多次,因此使用模板一
check
条件为a[mid] <= target
,若要找的是数字最后出现的位置,则使用模板二,check
条件为a[mid] >= target
stl里封装了二分相关的函数
lower_bound( begin,end,num,greater<type>())
和 upper_bound( begin,end,num,greater<type>())
lowerbound
可以查找小于等于num
的最小数的地址,不存在则返回end指针
upper_bound
可以查找小于num
的最小数的地址,不存在啧返回end指针
思路
原数组排序后,这题对于每个a[i]
,其可形成数对的数b
必定能形成一段连续区间,然后区间长度就是这个数能形成数对的数量,因此可以用二分从后往前找得出的下标减去从前往后找得出的下标的差值+1,也可以使用stl自带的二分函数(其底层和acwing板子有一定差异)
代码实现
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N];
int main() {
int n, c, ans = 0;
cin >> n >> c;
for (int i = 0; i < n; ++i)
cin >> a[i];
sort(a, a + n);
for (int i = 0; i < n; ++i)
ans += (upper_bound(a, a + n, a[i] + c) - a) - (lower_bound(a, a + n, a[i] + c) - a);
cout << ans;
}
当然这两个函数都可以通过上面的基础板子修改得到
upper_bound
函数可以用以下代码实现
[&](){
ll l = i, r = n - 1;
while (l <= r) {
ll mid = l + r >> 1;
if (a[i] + c < a[mid])
r = mid - 1;
else
l = mid + 1;
}
return l;
}()
lower_bound
函数可以用以下代码实现
[&](){
ll l = i, r = n - 1;
while (l <= r) {
ll mid = l + r >> 1;
if (a[i] + c <= a[mid])
r = mid - 1;
else
l = mid + 1;
}
return l;
}()
这题也可以使用双指针实现
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
int n , c;
int a[N];
int main () {
cin >> n >> c;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
sort(a + 1 , a + 1 + n);
int l = 1, r1 = 1 , r2 = 1;
ll ans = 0;
for(l = 1 ; l <= n ; l ++) {
while(r1 <= n && a[r1] - a[l] <= c) r1 ++;
while(r2 <= n && a[r2] - a[l] < c ) r2 ++;
ans += r1 - r2;
}
cout << ans;
return 0;
}
其他解法
比较简单的就是用桶来做,把所有a[i]
的值用桶存起来,再求出桶中a[i] - c
对应的桶的数量即可
因为数据范围可以达到 map
实现,或者用数组+离散化
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N];
int main() {
map<int, int> b;
int n, c;
ll ans = 0;
cin >> n >> c;
for (int i = 0; i < n; ++i) {
cin >> a[i];
b[a[i]]++;
}
for (int i = 0; i < n; ++i) {
if (b.count(a[i] - c))
ans += b[a[i] - c];
}
cout << ans;
}
本文作者:七つ一旋桜
本文链接:https://www.cnblogs.com/poifa/p/15191975.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步