01 二分查找算法笔记及刷题题解

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 刷题目录
  1. 数的范围789
  2. 剑指offer 数组中重复的数字
  3. leetcode704 二分查找
3.2 题目以及题解
  1. 数的范围789
    题目:
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。如果数组中不存在该元素,则返回 -1 -1。

输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n个整数(均在 110000110000 范围内),表示完整数组。
接下来 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;
}
  1. 剑指offer 数组中重复的数字
    题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:23 
限制:
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;
				}
			}
		}
};
  1. 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;
        }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值