二分查找-讲解与例题-C++

二分查找

整数二分

模板

  • 99 % 99\% 99%​的整数二分都可以用以下两个模板求得答案
  • c h e c k ( ) check() check()函数代表一个分界点,根据分界点,我们可以把序列分为两部分

模板一

while (l < r)
{
  	int mid = l + r >> 1;
  
  	if (check(mid)) r = mid;
  	else l = mid + 1;
}

模板二

while (l < r)
{
  	int mid = l + r + 1 >> 1;
  
  	if (check(mid)) l = mid;
    else r = mid - 1;
}

例题与讲解

根据模板,来解决一道实际问题

现有非严格递增序列 a = { 5 , 7 , 7 , 7 , 8 , 10 } a = \{5,7,7,7,8,10\} a={5,7,7,7,8,10},希望在给定一个目标值 x x x,返回序列中 x x x第一个和最后一个位置

假设 x = 7 x = 7 x=7

首先,我们看一下如何找 x x x在序列中的第一个位置;我们根据序列的性质,将序列分为两部分

   |-------------
5, 7, 7, 7, 8, 10
l		mid     r
  • 可以观察到,序列的左边的值,为 < x \lt x <x的部分;序列右边,为 ≥ x \ge x x​的部分

  • 因此,我们可以将 c h e c k ( m i d ) → a [ m i d ] > = x check(mid) \rightarrow a[mid] >= x check(mid)a[mid]>=x

  • 这个分法的分界点即为 x x x出现的第一个位置

  • 从一开始范围的变化进行思考,假设 m i d mid mid在最开始的位置如上

    • 可以发现, a [ m i d ] a[mid] a[mid]满足 c h e c k ( ) check() check()
    • 此时,我们要逼近分界点,则需要令 r = m i d r = mid r=mid
  • 因此发现,根据模板,在求 x x x第一次出现的位置时,我们可以用模板一

阶段代码如下:

int l = 0, r = a.size() - 1;

while (l < r)
{
  	int mid = l + r >> 1;
  
  	if (a[mid] >= x) r = mid;
  	else l = mid - 1;
}

接着,我们看一下如何找 x x x在序列中的最后一个位置

---------|  
5, 7, 7, 7, 8, 10
l    mid        r
  • 我们可以发现,令 c h e c k ( m i d ) → a [ m i d ] < = x check(mid) \rightarrow a[mid] <= x check(mid)a[mid]<=x,可以将序列分为两部分
  • 同样,从一开始范围的变化进行思考,假设 m i d mid mid在最开始的位置如上
    • 此时 a [ m i d ] a[mid] a[mid]满足 c h e c k ( ) check() check()函数,要逼近分界点,则需要令 l = m i d l = mid l=mid
  • 因此发现,根据模板,在求 x x x最一后次出现的位置时,我们可以用模板二

阶段代码如下:

int l = 0, r = a.size() - 1;

while (l < r)
{
  	int mid = l + r + 1 >> 1;
  
  	if (a[mid] <= x) l = mid;
    else l = mid - 1;
}

总结与完善代码

整数二分的流程大致如下:

  1. 确定 c h e c k ( ) check() check()​​函数,即找到一个分界点将序列分为两部分
  2. 思考当满足 c h e c k ( ) check() check()函数时,第一步该怎么走
  3. 第一步的走法确定之后,即可找到对应的模板,直接写就行了

例题链接:34. 在排序数组中查找元素的第一个和最后一个位置

完整代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int x) {
        if (nums.size() < 1) {
            return {-1, -1};
        }

		// 查找x出现的第一个位置
        vector<int> res;
        int l = 0, r = nums.size() - 1;
        while (l < r)
        {
            int mid = l + r >> 1;

            if (nums[mid] >= x) r = mid;
            else l = mid + 1;
        }

		// 未找到,代表序列中不存在x
        if (nums[l] != x) 
        {
            return {-1, -1};
        } 
        
        res.push_back(l);

		// 查找x出现的最后一个位置
		// 这里不更新l的原因是因为:
		// 当前l在x出现的第一个位置上,x最后出现的位置一定是在现在的l和更新后的r内部
        r = nums.size() - 1;
        while (l < r) 
        {
            int mid = l + r + 1 >> 1;

            if (nums[mid] <= x) l = mid;
            else r = mid - 1;
        }

        res.push_back(l);

        return res;
    }
};

浮点数二分

l , r l,r l,r d o u b l e double double f l o a t float float​类型,直接直觉上的判断处理即可,可根据代码理解

退出循环的条件为 ∣ r − l ∣ ≤ ξ |r - l| \le \xi rlξ

典型例题:求数的三次方根

− 10000 ≤ x ≤ 10000 -10000 \le x \le 10000 10000x10000

#include <iostream>

using namespace std;

int main()
{
    double x;
    cin >> x;

    double l = -100, r = 100;
    while (r - l > 1e-8) // 相当于最后三次方根的一个精度
    {
        double mid = (l + r) / 2;
        if (mid * mid * mid >= x) r = mid;
        else l = mid;
    }

    printf("%.6lf\n", l);
    return 0;
}
  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据库E-R图是用来描述数据库中数据实体、关系和属性之间关系的一种图形化表示方法。它是数据库设计阶段的重要工具,可以帮助开发人员更好地理解和设计数据库结构。 E-R图主要由实体、关系和属性三个部分组成。 实体是指数据库中的具体事物,如学生、课程、教师等。每个实体都具有一组属性,用来描述该实体的特征。例如,学生实体可以有姓名、学号、年龄等属性。 关系是指不同实体之间的联系。关系可以是一对一、一对多或多对多的。例如,学生和课程之间的关系可以是一对多的关系,一个学生可以选修多门课程,而一门课程可以由多个学生选择。 属性是实体或关系的特征或性质。属性可以是简单属性,即不可再分的属性,也可以是复合属性,由多个简单属性组成。例如,一个学生的姓名、年龄、电话号码等都是学生实体的属性。 通过E-R图,可以清晰地描述数据库中实体、关系和属性之间的关系。在E-R图中,实体一般用矩形框表示,关系用菱形表示,属性用椭圆形表示。通过箭头或线段来表示实体之间的关系类型。 例题解析讲解就是通过具体的例子来演示如何使用E-R图进行数据库设计。通过解析例题可以了解如何根据需求将实体、关系和属性进行合理的组织和设计。同时,例题解析还可以帮助理解E-R图的绘制规则和表示方法,以及不同类型关系的处理方式。 总的来说,数据库E-R图是数据库设计的重要工具,通过它可以清晰地描述数据库中实体、关系和属性之间的关系。通过例题解析,我们可以更好地理解和应用E-R图进行数据库设计。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值