AcWing算法学习之二分法

简介: AcWing算法学习之二分法

系列文章目录

第一节:快速排序和归并排序

第二节:二分之整数二分和小数二分

@TOC


前言

第二节课我想给大家整理一下AcWing上边的二分系列的算法给大家.
二分本身就是数学的一种思想,列入数学的二分法求根的范围题目,到了编程这里就成为了一种思想算法,当然也不难,只要大家认真学习,今天就可以拿下这个知识点。


一、整数二分

我们先来说一下单调性和二分的区别,有单调性一定能进行二分排序,但是能进行二分排序的不一定有单调性。所以说二分的本质并非是单调性。
在这里插入图片描述

情况1.

我们假设有这样一个范围。我们能把它分成两个部分,左边不满足某一个条件,右面满足某一个条件。我们用二分法可以找到左边部分的边界和右边部分的边界。我们的红色部分和蓝色部分就是我们整数二分的两个不同的模板了。
在这里插入图片描述
我们来解释一下:

我们取中间值mid,如果mid在红色区域部分,我们通过条件去检查这个Mid发现它满足条件的话,那我们就把这个范围设定成mid到r的区域,如果mid不满足条件,那么就把范围设定在l到mid-1的区域。这样就把原来的区域一分为二了。

如果是这种情况我们来看一下这个模板:记得mid这里要加1

mid=(l+r+1)/2

这种情况是mid属于右面的是使用第二个模板:

**// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
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;

}**

情况2.

在这里插入图片描述
这种时候Mid在蓝色区域,满足条件就是1到Mid范围区间,反之是mid+1到r区间。

这地方确实我听课的时候没完全理解,这部分理解起来很难,所以可以找一个简便的方式去记下来,我们可以把mid在红色区域看成左边界,在蓝色区域看成是右边界,这样记的话,Mid在红色区域满足条件的范围是mid到r,在蓝色区域满足条件的范围是l到mid。

它的模板是:

**// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{

while (l < r)
{
    int mid = l + r >> 1;
    if (check(mid)) r = mid;    // check()判断mid是否满足性质
    else l = mid + 1;
}
return l;

}**

我们的条件是:
bool check(int x) {/ ... /} // 检查x是否满足某种性质

所以我们在做题的时候就是看区间是l=mid还是r=mid再来选取对应的模板。

我们现在来说一个问题就是在第一种情况时为什么Mid要加1的问题,因为当我们的l不巧正好等于r-1的时候,如果不加1,我们这里都是向下取整的,那Mid就还是l,那我们每次循环完,范围还是l到r就会发生死循环,如果我们加上1,那么就不一样了,mid就是r,我们最开始是l到r,经过一次循环的时候范围变成了r到r,就停止了。

二、小数二分

方法1---根据题目法

在这里插入图片描述
题目要求就是精确到小数点六位那我一般的做法是往后面多精确两位这样会使得我的结果更加准确.

这个重点是什么呢?当然是while循环里面的r-l>谁了,记住这一点啥也不用怕了就.

方法2迭代法

在这里插入图片描述
那就是for循环直接迭代一百次这样,emm看到这里其实会发现小数二分法很简单和固定.

三习题练习.

1.整数二分查找

在这里插入图片描述

#include<stdio.h>
int main()
{
    int n,q,arr[100010];
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&arr[i]);
    scanf("%d",&q);
again:
    while(q--)
    {
        int x;
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r)
        {
            int mid=(l+r)/2;
            if(arr[mid]>x) r=mid;
            else if(arr[mid]<x) l=mid+1;
            else
            {
                printf("YES\n");
                goto again;
            } 
            
        }
        if(arr[l]!=x) printf("NO\n");
    }
    return 0;
}

整数二分法简简单单做出来,其实就是套模板对不对?

2.数的范围在这里插入图片描述

#include<stdio.h>
int main()
{
    int n,q,arr[100010];
    scanf("%d %d",&n,&q);
    for(int i=0;i<n;i++) scanf("%d",&arr[i]);
    while(q--)
    {
        int x;
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r)
        {
            int mid=(r+l)/2;
            if(arr[mid]>=x) r=mid;
            else l=mid+1;
        }
        if(arr[l]!=x) printf("-1 -1\n");
        else 
        {
            printf("%d ",l);
            
            int l=0,r=n-1;
            while(l<r)
            {
                int mid=(r+l+1)/2;
                if(arr[mid]<=x) l=mid;
                else r=mid-1;
            }
            printf("%d \n",l);
        }
    }
    return 0;
}

套用模板就能写出来这个题目,是不是很轻松捏?

3.小数二分法

在这里插入图片描述

//写法一.
#include<stdio.h>
#include<math.h>
#include<string.h>
double sum(double x)
{
    return (x*x*x*x*x)-15*(x*x*x*x)+ 85*(x*x*x)- 225*(x*x)+ 274*x-121;
}
int main()
{
    double l=1.5,r=2.4,n,mid;
    while(fabs(r-l)>0.000000001)
    {
        mid=(l+r)/2;
        n=sum(mid);
        if(n>0)
        {
            l=mid;
        }
        else if(n<0)
        {
            r=mid;
        }
        else
        {
            break;
        }
    }
    printf("%.6lf",mid);
    return 0;
}
//写法二其实大同小异仅此而已.
#include<stdio.h>
double sum(double x)
{
    return (x*x*x*x*x)-15*(x*x*x*x)+ 85*(x*x*x)- 225*(x*x)+ 274*x-121;
}
int main()
{
    double l=1.5,r=2.4,n,mid;
    for(int i=0;i<100;i++)
    {
        mid=(l+r)/2;
        n=sum(mid);
        if(n>0) l=mid;
        else if(n<0) r=mid;
        else break;
    }
    printf("%.6lf",mid);
    return 0;
}

本质与总结.

二分的本质是边界:只要找到某种性质,使得整个区间一分为二,那么就可以用二分把边界点分出来
在这里插入图片描述
这个是我后面在acwing网站自己总结的一些思想,其实就是上边的汇总,大家可以就是学习一下。

总结自己对写题的看法:
**在你没学习二分之前你会觉得哇啊题目好难,但是如果你学了这些思想再去刷题是不是会事半功倍捏?所以在这里推荐一种学习思路给大家,就是先学而后可以练习学到的算法思想这样才会进步更快。
第二就是写一道题只给自己15min想思路的时间如果没想到就去看题解去总结学习题解,不然更多的时间只是浪费时间好吧,如果不赞同请写出你的方法蟹蟹!**

相关文章
|
12天前
|
算法 JavaScript 前端开发
第一个算法项目 | JS实现并查集迷宫算法Demo学习
本文是关于使用JavaScript实现并查集迷宫算法的中国象棋demo的学习记录,包括项目运行方法、知识点梳理、代码赏析以及相关CSS样式表文件的介绍。
第一个算法项目 | JS实现并查集迷宫算法Demo学习
|
16天前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
16 2
|
2月前
|
机器学习/深度学习 人工智能 资源调度
【博士每天一篇文献-算法】连续学习算法之HAT: Overcoming catastrophic forgetting with hard attention to the task
本文介绍了一种名为Hard Attention to the Task (HAT)的连续学习算法,通过学习几乎二值的注意力向量来克服灾难性遗忘问题,同时不影响当前任务的学习,并通过实验验证了其在减少遗忘方面的有效性。
54 12
|
2月前
|
算法 Java
掌握算法学习之字符串经典用法
文章总结了字符串在算法领域的经典用法,特别是通过双指针法来实现字符串的反转操作,并提供了LeetCode上相关题目的Java代码实现,强调了掌握这些技巧对于提升算法思维的重要性。
|
2月前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks
本文提出了一种基于任务条件超网络(Hypernetworks)的持续学习模型,通过超网络生成目标网络权重并结合正则化技术减少灾难性遗忘,实现有效的任务顺序学习与长期记忆保持。
35 4
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之RWalk:Riemannian Walk for Incremental Learning Understanding
RWalk算法是一种增量学习框架,通过结合EWC++和修改版的Path Integral算法,并采用不同的采样策略存储先前任务的代表性子集,以量化和平衡遗忘和固执,实现在学习新任务的同时保留旧任务的知识。
76 3
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-综述】基于脑启发的连续学习算法有哪些?附思维导图
这篇博客文章总结了连续学习的分类,包括经典方法(重放、正则化和稀疏化方法)和脑启发方法(突触启发、双系统启发、睡眠启发和模块化启发方法),并讨论了它们在解决灾难性遗忘问题上的优势和局限性。
31 2
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
113 9
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战