AcWing789.数的范围

概要

谨以博客记录我的算法学习之旅,这是我学习的第一个算法,二分,这个算法虽然看起来简单,但是极其容易出错,模板只是起到辅助的作用,如果想真正搞懂,一定要在模板的基础上刷题和自己动手去模拟,这样的时间长了后即使不需要模板也可以信手拈来了。

为什么要引入二分

顺序查找的速度属实太慢,分分钟超时,所以需要二分来加快速度

何为二分?

二分大概就是在一个数组有序的情况下,每次都查找中间的值,判断其大小,如果大于,则找其左边的,如果小于,则找其右边的,话不多说,我们先上模板

二分模板

//查找左边界 SearchLeft 简写SL
int SL(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid; 
        else l = mid + 1; 
    }   
    return l;
}
//查找右边界 SearchRight 简写SR 
int SR(int l, int r) 
{
    while (l < r)
    {                   
        int mid = l + r + 1 >> 1; //需要+1 防止死循环
        if (check(mid)) l = mid;
        else r = mid - 1; 
    }
    return r; 
}

作者:CPT1024
链接:https://www.acwing.com/solution/content/107848/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这个是我在acwing上看到的一位大佬写的模板,看起来复杂而又不复杂,哈哈哈我在说什么呀,我大概解释一下,SL函数就是查找其左边界(即从左边开始第一个大于等于需要找的值的那个位置,SR函数则是查找其右界(即最后一个大于等于需要找的那个值的位置,当然两边也可以换成小于,根据题目来区分,不过这些需要多多做题练习,模板为啥要这样子做呢?且听我分析。

*SL:也就是刚刚说的找从左边开始第一个大于等于ans的位置,那么这里的check(mid)就应该改为a[mid]>=ans,而为什么这里要等于并且下面的l要加1呢,原因是因为既然我们要找第一个大于等于它的数字,那么我们就希望我们现在middle的位置在尽可能左边一点,只有这样子才能找到第一个,所以如果a[middle]<ans的话,l=middle+1,因为a[middle]已经小于ans了,那么这个middle不要也罢,直接从它的下一位开始判断,而另外一边为什么要取等于号呢?因为只有这样子才能确保那个等于ans的middle我们找到了,而且在把这个我们找到的值往左边缩小,一直缩小到我们需要的那个最开始的值。

SR:SR就不用多说了,为什么要除以二那里要加1呢,原因就是计算机中的整形除法会丢精度,这样会陷入死循环,大家可以自己去试试,其他的不赘述,跟上面的差不多。

话不多说,我们来做一道题。

题目以及题解

在这里插入图片描述
题解(套模板即可):

import java.util.Scanner;

public class p789 {
    public static void main(String []args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int q=sc.nextInt();
        int []a=new int[n];
        for (int i = 0; i < n; i++) {
            a[i]=sc.nextInt();
        }
        while (q-->0){
            int start=0;
            int end=a.length-1;
            int ans=sc.nextInt();
           while(start<end){   //查找第一个相等的
               int middle=(start+end)>>1;
               if(a[middle]>=ans) end=middle;
               else start=middle+1;
           }
            if(a[start]!=ans){
                System.out.printf("%d %d",-1,-1);
                System.out.println();
            }
            else {
                int num1=start;
                start=0;
                 end=a.length-1;
                while(start<end){    //求最后一个,也就是右边界
                    int middle=(start+end+1)>>1;
                    if(a[middle]<=ans) start=middle;
                    else end=middle-1;
                }
                System.out.printf("%d %d",num1,end);
                System.out.println();
            }
        }
    }
}

小结

大家多多练习吧,我只是一个小白,不太会表达,写的不好的地方请多多指教,谢谢,希望能帮助到大家。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值