算法|1.二分及其扩展

news2024/11/25 2:27:53

算法|1.二分及其扩展

1、有序数组中找到num

题意:给定有序数组,在有序数组中找到指定数字,找到返回true,找不到返回false.

解题思路:

  • 数组有序查找指定元素使用二分法
  • L指针初始值设为0,R指针初始值设为arr.length-1,属于左闭右闭的区间,循环条件为L<=R

优化的点:

  • 求平均值使用有符号右移:mid=left+((right-left)>>1).好处有两点1.防止溢出2.提高效率

对数器:

  • 使用顺序遍历
  • 前提:生成随机数组

核心代码:

public static boolean exist(int[] arr,int num){
    int L=0;
    int R=arr.length-1;
    int M=L+((R-L)>>1);
    while(L<=R){
        if(arr[M]<num){
            L=M+1;
        }else if(arr[M]>num){
            R=M-1;
        }else{
            return true;
        }
        M=L+((R-L)>>1);
    }
    return false;
}

测试代码:

    //for test
    public static boolean test(int[] arr,int num){
        for (int cur:arr) {
            if(cur==num){
                return true;
            }
        }
        return false;
    }
    //for test
    public static int[] generateRandomArray(int maxSize,int maxValue){
        int[] arr=new int[(int) ((maxSize+1)*Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i]= (int) ((maxValue+1)*Math.random())-(int) ((maxValue+1)*Math.random());
        }
        return arr;
    }

    //for test
    public static void print(int[] arr){
        for (int cur:arr) {
            System.out.print(cur+" ");
        }
        System.out.println();
    }
//    //for test
    public static void main(String[] args) {
        int testTime=1000;
        int maxSize=10;
        int maxValue=100;
        boolean succeed=true;
        for (int i = 0; i < testTime; i++) {
            int[] arr=generateRandomArray(maxSize,maxValue);
            int num=(int) ((maxValue+1)*Math.random());

            //使用前提:数组是有序的
            Arrays.sort(arr);

            boolean ret1=exist(arr,num);
            boolean ret2=test(arr,num);
            if(ret1!=ret2){
                print(arr);
                System.out.println("num:"+num);
                System.out.println("exist:"+ret1);
                System.out.println("test:"+ret2);
                System.out.println("Oops!Error!");
                succeed=false;
                break;
            }
        }
        if(succeed==true){
            System.out.println("succeed!");
        }
    }

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0HyhetxX-1685076234668)(F:\typora插图\image-20230526103704932.png)]

果然,人长时间不写代码,脑子是会秀逗的(…)

你敢信这是一开始写的??真该死啊…

public static boolean exist(int[] arr,int num){
    int L=0;
    int R=arr.length-1;
    int M=L+((R-L)>>1);
    while(L<=R){
        if(arr[L]<num){
            L=M+1;
        }else if(arr[R]>num){
            R=M-1;
        }else{
            return true;
        }
        M=L+((R-L)>>1);
    }
    return false;
}

还debug半天反应不过来

    //for test
    //1.使用前提:必须是有序的
    //2.sort排序不生效——得是M和Num比啊!!!
//    public static void main(String[] args) {
//        int[] arr={-15, -14 ,4 ,24, 26, 33, 81  };
//        Arrays.sort(arr);
//        if(exist(arr,4)!=test(arr,4)){
//            System.out.println(false);
//        }else{
//            System.out.println(true);
//
//        }
//    }

2、有序数组中找到>=num的最左边的位置

题意:给定有序数组,在有序数组中找到>=指定数字的最左边的下标,找到返回对应值,找不到返回-1.

解题思路:

  • 注意:这里并没有要求value值必须存在于指定数组
  • 数组有序查找指定元素下标使用二分法
  • 在原来的基础上增加一个变量记录当前查找元素下标,如果当前值大于等于指定值,则记录下标。最终返回的结果只可能是两种值-1,符合题意的值

优化思路:

  • 没有采用在上一题的基础上增加变量记录当前坐标,然后再对这个坐标进行操作,而是直接在分支条件上动手,记录并迭代更新,这样只需要在返回处进行判断是否存在这样的值即可。

对数器:

  • 顺序遍历

核心代码:

public static int nearestIndex(int[] arr,int num){
    int L=0;
    int R=arr.length-1;
    int M=L+((R-L)>>1);
    int index=-1;
    while(L<=R){
        if(arr[M]>=num){
            index=M;
            R=M-1;
        }else{
            L=M+1;
        }
        M=L+((R-L)>>1);
    }
    return index;
}

测试代码:

基本同上,但是需要修改比对方法和核心方法的返回值

//for test
public static int test(int[] arr,int num){
    for (int i = 0; i < arr.length; i++) {
        if(arr[i]>=num){
            return i;
        }
    }
    return -1;
}
//for test
public static int[] generateRandomArray(int maxSize,int maxValue){
    int[] arr=new int[(int) ((maxSize+1)*Math.random())];
    for (int i = 0; i < arr.length; i++) {
        arr[i]= (int) ((maxValue+1)*Math.random())-(int) ((maxValue+1)*Math.random());
    }
    return arr;
}

//for test
public static void printArray(int[] arr){
    for (int cur:arr) {
        System.out.print(cur+" ");
    }
    System.out.println();
}
//    //for test
public static void main(String[] args) {
    int testTime=1000;
    int maxSize=10;
    int maxValue=100;
    boolean succeed=true;
    for (int i = 0; i < testTime; i++) {
        int[] arr=generateRandomArray(maxSize,maxValue);
        int num=(int) ((maxValue+1)*Math.random());

        //使用前提:数组是有序的
        Arrays.sort(arr);

        int ret1=nearestIndex(arr,num);
        int ret2=test(arr,num);
        if(ret1!=ret2){
            printArray(arr);
            System.out.println("num:"+num);
            System.out.println("nearestIndex:"+ret1);
            System.out.println("test:"+ret2);
            System.out.println("Oops!Error!");
            succeed=false;
            break;
        }
    }
    if(succeed==true){
        System.out.println("succeed!");
    }
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOHyBbMu-1685076234669)(F:\typora插图\image-20230526110446203.png)]

3、有序数组中找到<=num最右的位置

题意:给定有序数组,在有序数组中找到<=指定数字的最右边的下标,找到返回对应值,找不到返回-1.

解题思路:

  • 数组有序,查找指定元素下标==>二分
  • 在分支条件上动手

优化思路:

  • 在分支条件上动手,同上题,合并两个分支条件,index初值设为-1

核心代码:

public static int nearestIndex(int[] arr,int num){
    int L=0;
    int R=arr.length-1;
    int M=L+((R-L)>>1);
    int index=-1;
    while(L<=R){
        if(arr[M]<=num){
            index=M;
            L=M+1;
        }else{
            R=M-1;
        }
        M=L+((R-L)>>1);
    }
    return index;
}

测试代码:

基本同上,但是注意这次对数器方法需要倒序遍历,找到返回,找不到返回-1

//for test
public static int test(int[] arr,int num){
    for (int i = arr.length-1; i >= 0; i--) {
        if(arr[i]<=num){
            return i;
        }
    }
    return -1;
}
//for test
public static int[] generateRandomArray(int maxSize,int maxValue){
    int[] arr=new int[(int) ((maxSize+1)*Math.random())];
    for (int i = 0; i < arr.length; i++) {
        arr[i]= (int) ((maxValue+1)*Math.random())-(int) ((maxValue+1)*Math.random());
    }
    return arr;
}

//for test
public static void printArray(int[] arr){
    for (int cur:arr) {
        System.out.print(cur+" ");
    }
    System.out.println();
}
//    //for test
public static void main(String[] args) {
    int testTime=1000;
    int maxSize=10;
    int maxValue=100;
    boolean succeed=true;
    for (int i = 0; i < testTime; i++) {
        int[] arr=generateRandomArray(maxSize,maxValue);
        int num=(int) ((maxValue+1)*Math.random());

        //使用前提:数组是有序的
        Arrays.sort(arr);

        int ret1=nearestIndex(arr,num);
        int ret2=test(arr,num);
        if(ret1!=ret2){
            printArray(arr);
            System.out.println("num:"+num);
            System.out.println("nearestIndex:"+ret1);
            System.out.println("test:"+ret2);
            System.out.println("Oops!Error!");
            succeed=false;
            break;
        }
    }
    if(succeed==true){
        System.out.println("succeed!");
    }
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9n5RfAR-1685076234670)(F:\typora插图\image-20230526110946676.png)]

4、局部最小值

题意:给定一个数组,已知任一相邻的数都不相等,找到随便一个局部最小值返回。

局部最小值定义:

定义何为局部最小值:
arr[0] < arr[1],0位置是局部最小;
arr[N-1] < arr[N-2],N-1位置是局部最小;
arr[i-1] > arr[i] < arr[i+1],i位置是局部最小;

解题思路:

  • 注意:1.是下标不是值2.边界条件判断:空数组、长度为1的数组、两端判断、中间判断,一共四部分逻辑处理!!!
  • 闭存在与不存在:是不是存在“凹”结构
  • 需要有左右两边淘汰的逻辑,使用二分
  • 遍历范围为可能成为局部最小值的位置:边界位置只需要比较一个位置而中间位置需要比较两个位置,为了统一处理,边界符合条件时单独判断,中间位置使用L、R、M三个指针进行遍历
  • (大体)分支条件的判断从原来的M下标和num比,改成M和M-1及M和M+1比较,两个需要调整的条件都是>,反之就是符合条件的局部最小值
  • (具体到某个分支)每次判断都是为砍范围,缩小范围与另外一边重新构成凹结构,砍到没法再砍了,直接返回对应下标(多结合实例)

优化思路:

  • 使用二分法,运用左右淘汰的逻辑
  • 每次三次比较到经典二分三次比较的套用

对数器:

  • 遍历?错

    可能存在多个局部最小值,所以不能通过比对返回的下标,而是拿着下标去验证是不是!!

    当然再次之前需要判断下标是否合法

    这里的对数器可以命名为isRight,返回值为布尔类型

    优化

    • 可以根据index分类讨论:中间有效性、两端有效性
    • 中间index判断可以用三目表达式

核心代码:

注意调整新三分支中的调整条件:

Q1:每次怎么调整?既然没有要求数组有序,那是不是只要mid可以改变就可以,是不是L=M+1和R=M-1的调整条件可以交换?

不是!!

经过开头的预处理,说明该数组上一定存在“凹”结构,那么如果M左边的位置小于它,那么就破坏了整体的这个结构,我们就从右边找,此时右边的结构满足,对应的左边的结构满足,每次把破坏结构的一半砍下去,也一定能够找出来。

Q2:这样一定能找出结果吗?

能的,这是一个算法的结论:即不存在相同元素的数组,一定存在局部最小值

public static int getLessIndex(int[] arr){
    if(arr==null||arr.length==0){
        return -1;
    }
    if(arr.length==1||arr[0]<=arr[1]){
        return 0;
    }
    if(arr[arr.length-1]<=arr[arr.length-2]){
        return arr.length-1;
    }
    int L=1;
    int R=arr.length-2;
    int M=L+((R-L)>>1);
    while(L<=R){
        if(arr[M]>arr[M-1]){//砍左边
            R=M-1;
        }else if(arr[M]>arr[M+1]){//砍左边
            L=M+1;
        }else{
            return M;
        }
        M=L+((R-L)>>1);
    }
    return -1;
}

测试代码:

  • 数组不需要严格有序
  • 数组中不能有重复元素——赋值时做一个do while循环
//for test
public static boolean isRight(int[] arr,int index){
    if(arr.length<=1){
        return true;
    }
    if(index==0){
        return arr.length==1||arr[0]<arr[1];
    }
    if(index==arr.length-1){
        return arr[index]<arr[index-1];
    }
    return arr[index] < arr[index - 1] && arr[index] < arr[index + 1];
}
//for test
public static int[] generateRandomArray(int maxSize,int maxValue){
    int[] arr=new int[(int) ((maxSize+1)*Math.random())+1];
    arr[0]= (int) ((maxValue+1)*Math.random())-(int) ((maxValue+1)*Math.random());
    for (int i = 1; i < arr.length; i++) {
        do {
            arr[i]= (int) ((maxValue+1)*Math.random())-(int) ((maxValue+1)*Math.random());
        }while(arr[i]==arr[i-1]);
    }
    return arr;
}

//for test
public static void printArray(int[] arr){
    for (int cur:arr) {
        System.out.print(cur+" ");
    }
    System.out.println();
}
//for test
public static void main(String[] args) {
    int testTime=1000;
    int maxSize=10;
    int maxValue=100;
    boolean succeed=true;
    for (int i = 0; i < testTime; i++) {
        int[] arr=generateRandomArray(maxSize,maxValue);

        int index=getLessIndex(arr);
        if(!isRight(arr,index)){
            printArray(arr);
            System.out.println("getLessIndex:"+index);
            System.out.println("Oops!Error!");
            succeed=false;
            break;
        }
    }
    if(succeed==true){
        System.out.println("succeed!");
    }
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HLlEoZ5m-1685076234670)(F:\typora插图\image-20230526123206885.png)]

二分法总结

算法描述:不断对闭区间(其实有时候处理的是半开半闭区间、开区间)一分为二的方法。

基本思想:一分为二

使用场景:

  • 有序数组查找指定元素/下标
  • 无序数组(满足左右淘汰逻辑)查找指定元素/下标

例题总结:

  • 有序数组查找指定元素:数组必须预处理保证有序,三分支均为arr[M]与num比较,调整放到分支外
  • 有序数组查找>=num最左边的位置:即查找数组中大于等于num的最小值的下标。其中一和arr[M]>num分支合并,不断更新,更新方向R=M-1
  • 有序数组查找<=num最右边的位置:即查找数组中小于等于num的最大值的下标。其中一和arr[M]<num分支合并,不断更新,更新方向L=M+1
  • 局部最小值:空数组、长度为1的数组、两端判断、中间判断,一共四部分逻辑处理,缩小范围的调整结合趋势图分析,实在不行再看运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/571449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python修改配置文件内容

Python 修改配置文件内容 介绍 配置文件是软件开发中经常使用的一种文件&#xff0c;用于存储程序的配置参数。在实际开发中&#xff0c;我们经常需要修改配置文件内容来满足我们的需求。Python 提供了很多方法来操作配置文件&#xff0c;让我们能够方便地修改配置文件。 本…

camunda如何部署到容器云Kubernetes

部署Camunda到Kubernetes需要以下步骤&#xff1a; 1、将Camunda打包成Docker镜像。 2、在Kubernetes中创建一个Deployment对象&#xff0c;用于定义Camunda应用程序的副本数量、容器镜像、环境变量、卷挂载等信息。例如&#xff0c;可以使用以下命令创建一个Deployment&…

镜像二叉树和求二叉树最大深度(java)

镜像二叉树和求二叉树最大深度 镜像二叉树。有些题目叫翻转二叉树。是同一个题。二叉树的最大深度 镜像二叉树。有些题目叫翻转二叉树。是同一个题。 题目描述&#xff1a;给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例&#xff1…

从Redisson的RedissonSemaphore引发的信号量实际含义的思考

Semaphore到底该如何使用 事情的起因是最近在看redisson的源码&#xff0c;刚好看到了RedissonSemaphore的acquire/release实现。 public RFuture<Void> releaseAsync(int permits) {if (permits < 0) {throw new IllegalArgumentException("Permits amount ca…

微信小程序 基础模板引入sass的两种方法

推荐使用第二种方法 一、VSCode扩展引入&#xff08;旧&#xff09; 1.vscode搜索扩展 Easy Sass安装 2.微信开发者工具导入vscode安装的所有扩展 3.修改sass扩展配置 打开扩展目录 找到刚导入的sass扩展 打开package.json文件 改成这样 保存 4.重新打开此项目 配置完事 5.使…

torch.distributed.launch多卡多机

torch.distributed.launch命令介绍 我们在训练分布式时候&#xff0c;会使用到 torch.distributed.launch 可以通过命令&#xff0c;来打印该模块提供的可选参数 python -m torch.distributed.launch --help usage: launch.py [-h] [--nnodes NNODES] [--node_rank NODE_RANK]…

Flutter_环境配置

FlutterSDK 下载FlutterSDK管理工具<SideKick>下载安装<SideKick>下载FlutterSDK设置全局SDK 修改Flutter配置文件获取全局SDK路径 验证配置是否成功验证环境配置 下载FlutterSDK管理工具 下载安装 SideKick下载链接 下载FlutterSDK 打开 SideKick选择需要的SD…

基于Python/MATLAB长时间序列遥感数据处理及在全球变化、物候提取、植被变绿与固碳分析、生物量估算与趋势分析

目录 专题一、长时序遥感产品在全球变化/植被变绿/植被物候等方面的应用 专题二、MODIS遥感数据产品预处理 专题三、长时序MODIS遥感数据产品时间序列重构 专题四、基于GIMMS 3g和MODIS NDVI构建更长时序遥感数据 专题五、植被物候提取与分析实践应用 专题六、植被变绿趋…

卷S人的166页精品Java面试手册,17大java面试系列专题让你全方位暴击大厂Java面试官!

你有面试机会了吗&#xff1f; 近期&#xff0c;肯定有很多小伙伴&#xff0c;投出去的简历HR基本上都是已读不回&#xff0c;甚至都没有任何回复&#xff0c;或者平台默认筛选&#xff0c;你的简历HR根本就看不到。 即使有些小伙伴简历通过&#xff0c;收到面试邀请了&#…

RestCloud荣膺广东省优秀软件产品奖,引领国内数据集成领域!

近日&#xff0c;“2022年广东软件风云榜”名单公布&#xff0c;“谷云ETL数据交换软件”凭借其在助力企业数字化转型升级过程中的卓越表现&#xff0c;荣获由羊城晚报报业集团、广东软件行业协会、广东省大数据协会联合颁发的“优秀软件产品和解决方案”奖。 数字化转型是推动…

【CCF- CSP 202104-2 邻域均值 二维数组前缀和满分题解】

代码思路&#xff1a; 本题如果直接用暴力求解的话只能得70分。 运用到了二维数组的前缀和&#xff0c;难点是如何求出二维数组的前缀和并计算出领域所有元素的和。 注意计算平均数的时候要保证精度相同&#xff0c;所有都要化为double型&#xff0c;否则会出错。 首先&…

探索商机,连接世界——第133届广交会买家信息帮你快速找到合适的客户

亲爱的商家和供应商们&#xff01;&#xff01; 您是否在寻找拓展市场和国际贸易的机会&#xff1f;你想找到合适的客户&#xff0c;推广你的产品和服务&#xff0c;取得更大的商业成功吗&#xff1f;那么&#xff0c;我们给你带来了一个难得的机会&#xff01; 随着第133届广…

【开源项目】Dynamic-Tp告警系统的源码解析

序言 【开源项目】Dynamic-Tp核心流程源码解读&#xff0c;继上回解读完DynamicTp这个开源项目的源码&#xff0c;觉得其中的告警机制也是十分精彩&#xff0c;如果能学会&#xff0c;用在自己的项目中&#xff0c;那才能说得上掌握了DynamicTp这个开源项目的源码理解的精髓。…

零碳光储 数能未来 | 全系光储产品实力吸睛,科士达精彩亮相SNEC 2023

5月24日&#xff0c;光伏行业最具影响力的全球性展会——“SNEC 2023”在上海盛大开幕。作为行业领先的全能方案供应商&#xff0c;科士达以“零碳光储 数能未来”为主题&#xff0c;携全系光储产品及解决方案重磅亮相。 展会现场&#xff0c;科士达展出的全系解决方案涵盖分布…

APACHE-ATLAS-2.1.0简介(三)

APACHE-ATLAS-2.1.0简介(一) APACHE-ATLAS-2.1.0简介(二) 写在前面 ATLAS为组织提供开放式的元数据管理和治理功能&#xff0c;用以构建其数据资产目录&#xff0c;对这些资产进行分类和管理&#xff0c;形成数据字典。 名词解释 元数据&#xff1a;就是用于描述数据的数据…

js实现PDF 预览和文件下载

在开发过程中要求对 PDF 类型的发票提供 预览 和 下载 功能&#xff0c;PDF 类型文件的来源又包括 H5 移动端 和 PC 端&#xff0c;而针对这两个不同端的处理会有些许不同&#xff0c;下文会有所提及。 针对 PDF 预览 的文章不在少数&#xff0c;但似乎都没有提及可能遇到的问…

【Java-Crawler】SpringBoot集成WebMagic实现爬虫出现的问题集(一)

SpringBoot集成WebMagic实现爬虫出现的问题集&#xff08;一&#xff09; 一、SpringBoot集成WebMagic框架日志异常问题及解决方案二、使用 Firefox 驱动&#xff08;geckodriver&#xff09;三、设置WebMagic中site中的User-Agent&#xff08;避免反爬虫&#xff09; 一、Spri…

【网络编程】demo版TCP网络服务器实现

文章目录 一、引入二、服务端实现2.1 创建套接字socket2.2 绑定bind2.3 设置监听状态listen2.4 获取新链接accept2.5 获取信息与返回信息&#xff08;文件操作&#xff09; 三、客户端实现3.1 创建套接字socket3.2 绑定问题3.3 发起链接connect3.4 客户端并行3.4.1 多进程版3.4…

公网远程访问本地Jupyter Notebook服务

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自cpolar的文章&#xff1a;公网远程访问Jupyter Notebook【Cpolar内网穿透】 前言 Jupyter Notebook&am…

针对UDP协议的攻击与防御

一、UDP协议概述 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是TCP/IP协议栈中的一种无连接的传输协议&#xff0c;能够提供面向事务的简单不可靠数据传输服务。 1&#xff0e;UDP的报文格式 UDP的报文格式如图1所示。 图1 UDP报文格式 …