1 二分查找理论部分
1.二分查找应满足的条件:有序数组、(无重复元素)
2.二分查找解决的四类问题:(题型无论怎么变就这四类)
- 找>=x的第一个位置
- 找<=x的最后一个位置
- 查找最大值
- 查找最小值
3.二分查找流程 - 确定什么边界(left or right),就决定选用什么模板。
这里借用其他博主的图,这张图很清晰地表示了左右边界!
2 二分查找模板
但这里总结的模板仅限于整数,浮点数的我未总结。
- 左边界
bool check(int x) {/* ... */} // 检查x是否满足某种性质
左边界
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (a[mid]>=x) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
- 右边界
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:右边界
右边界
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (a[mid]<=x) l = mid;
else r = mid - 1;
}
return l;
}
3 实践是检验真理的唯一标准
这里总结刷题指南,但主要是基础的,进阶版的我打算把基础算法全部巩固加强完后再总结一篇刷题指南以及自己的题解。
3.1 刷题目录
- 数的范围789
- 剑指offer 数组中重复的数字
- leetcode704 二分查找
3.2 题目以及题解
- 数的范围789
题目:
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n个整数(均在 1∼100001∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
题解 此题重复元素,但有序。考虑用二分。确定边界问题,由于此题需返回起止位置,本质上求左右边界(最小最大值)问题。
代码
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int SL(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 l;
}
int SR(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() {
int n, q,k;
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
while (q--) {
cin >> k;
int index = SL(0, n - 1, k); //查找左边界 并返回下标index
if (a[index] != k) cout << "-1 -1"<< endl;
else {
cout << index << ' ';
cout << SR(0, n - 1, k) << endl;
}
}
return 0;
}
- 剑指offer 数组中重复的数字
题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
题解: 二分查找基本应用,这道题是无序的,不能直接使用二分,应先对其排序再二分。
代码
#include<iostream>
using namespace std;
class Solution {
public:
int findRepeatNumber(vector<int>& a) {
int len = a.size();
sort(a.begin(), a.end()); //先对其排序,满足二分查找条件
for (i = 0; i < len; i++) {
int l = 0, r = len - 1, x = a[i];
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
int L = l;
int l = 0, r = len - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x) l = mid;
else r = mid - 1;
}
int R = r;
if (L != R) {
return x;
}
}
}
};
- leetcode704 二分查找
题目:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
题解: 确定是什么边界,这里就是左边界,套用左边界模板即可。注意一下最后的条件就行。
代码
class Solution{
public:
int search(vector<int> & nums,int target){
int len = nums.size();
for(int i=0;i<len;i++){
int l=0,r=len-1;
while(l<r){
int mid = (l+r)/2;
if(nums[mid] >=target) r=mid;
else l=mid+1;
}
if(nums[i]==target) return l;
//之前卡在这一步导致有部分案例未通过
}
return -1;
}
};