LeetCode-刷题记录-二分法合集(本篇blog会持续更新哦~)

news2025/1/18 13:58:47

一、二分查找概述

二分查找(Binary Search)是一种高效的查找算法,适用于有序数组或列表。(但其实只要满足二段性,就可以使用二分法,本篇博客后面博主会持续更新一些题,来破除一下人们对“只有有序才能二分”的误解。)

在这里插入图片描述

二分通过反复将查找范围分为两半,并根据目标值与中间元素的大小关系来确定下一步查找的方向,从而快速定位目标值的位置。

二、二分法代码实现

三、二分法习题合集

1.LeetCode 35 搜索插入位置

在这里插入图片描述
在这里插入图片描述

  • 解法

在这里插入图片描述

public static int searchInsert(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    
    // 使用循环来查找目标值或确定插入位置
    while (left <= right) {
        int mid = left + (right - left) / 2; // 计算中间位置,避免整数溢出问题
        
        if (nums[mid] > target) {
            right = mid - 1; // 如果中间值大于目标值,缩小搜索范围至左半部分
        } else if (nums[mid] < target) {
            left = mid + 1; // 如果中间值小于目标值,缩小搜索范围至右半部分
        } else {
            return mid; // 找到目标值,直接返回索引
        }
    }
    
    // 循环结束时,left 指向应该插入的位置
    return left;
}

  • 这里博主解释一下为什么最后返回left(debug走一下流程或者在草稿纸上画一画其实就很容易看出来啦~)。

函数 searchInsert 的目标是在给定的有序数组 nums 中查找目标值 target 的插入位置(如果目标值不存在于数组中)。

如果数组中存在目标值,则返回目标值的索引;如果不存在,则返回应该插入的位置索引,使得插入后数组依然保持有序。

插入位置保持有序性

  • 返回 left 而不是 right 是因为当循环结束时,left 恰好指向比 target 大的第一个元素的位置,或者数组的末尾位置(如果 target 大于数组中的所有元素),这正是目标值应该插入的位置,可以保持数组的有序性。

2.LeetCode 69 x的平方

在这里插入图片描述

  • 解法

在这里插入图片描述

public static int mySqrt(int x) {
        if (x == 0 || x == 1) return x;
        int left = 0, right = x;
        int ans = 0;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            //防止越界~因为 mid*mid 数值过大 int可能会越界 或者 强转一下也可——(long)mid*mid
            if (mid < x / mid) { //如果这个整数的平方 严格小于 输入整数,那么这个整数 可能 是我们要找的那个数(重点理解这句话)。
                ans = mid;//所以我们更新答案
                left = mid + 1;
            } else if (mid > x / mid) { //如果这个整数的平方 严格大于 输入整数,那么这个整数  肯定不是  我们要找的那个数;
                right = mid - 1;
            } else { //如果这个整数的平方 恰好等于 输入整数,那么我们就找到了这个整数;
                return mid;
            }
        }
        return ans;
    }

3.LeetCode 367 有效的完全平方数

在这里插入图片描述

  • 解法

在这里插入图片描述

public static boolean isPerfectSquare(int num) {
    int left = 0, right = num;
    
    // 使用二分查找来确定是否为完全平方数
    while (left <= right) {
        int mid = left + (right - left) / 2; // 计算中间值,避免整数溢出问题
        
        if ((long) mid * mid < num) {
            left = mid + 1; // 如果 mid 的平方小于 num,说明目标值在右半部分,缩小搜索范围至右半部分
        } else if ((long) mid * mid > num) {
            right = mid - 1; // 如果 mid 的平方大于 num,说明目标值在左半部分,缩小搜索范围至左半部分
        } else {
            return true; // 如果 mid 的平方等于 num,直接返回 true,表示找到完全平方数
        }
    }
    
    // 循环结束时,未找到完全平方数,返回 false
    return false;
}


4.LeetCode 34 在排序数组查找元素的第一个和最后一个位置

在这里插入图片描述

  • 解法

本题拆分成两个函数,分别处理较好,不过这个对处理二分的熟练度要求还挺高,比如说left<=right 还是left<right以及左右指针什么时候该怎么移动都有讲究,一个小细节不对的话就得不到正确的答案。

大概的二分的模版大家都知道,区别就在于具体问题的边界问题,是需要自己思考的。

建议有电脑的小伙伴可以debug走一下流程 ,可以看出来左右指针怎么移动的,慢慢调试;或者在纸上画一画,看一看自己写的二分是怎么个流程~


在这里插入图片描述

public static int[] searchRange(int[] nums, int target) {
    int[] ans = {-1, -1};
    // 特殊情况处理:数组为空,或者目标值不在数组范围内
    if (nums.length == 0 || nums[0] > target || nums[nums.length - 1] < target) return ans;
    
    // 查找第一次出现的位置
    int first = findFirst(nums, target);
    if (first == -1) return ans; // 如果找不到目标值,返回初始的{-1, -1}
    ans[0] = first;
    
    // 查找最后一次出现的位置
    ans[1] = findLast(nums, target);
    
    return ans;
}

// 找到元素第一次出现的位置
public static int findFirst(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1; // 目标值在右半部分
        } else if (nums[mid] >= target) {
            right = mid - 1; // 目标值在左半部分或者当前位置就是目标值
        }
    }
    // 当退出循环时,left 指向第一个大于等于目标值的位置
    return nums[left] == target ? left : -1; // 如果找到目标值,返回该位置;否则返回 -1
}

// 找到元素最后一次出现的位置
public static int findLast(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] <= target) {
            left = mid + 1; // 目标值在右半部分或者当前位置就是目标值
        } else if (nums[mid] > target) {
            right = mid - 1; // 目标值在左半部分
        }
    }
    // 当退出循环时,right 指向最后一个小于等于目标值的位置
    return right; // 直接返回 right,即最后一次出现的位置
}


5.LeetCode 33 搜索旋转排序数组

在这里插入图片描述

  • 解法

题目要求我们设计一个时间复杂度为O(logN)的算法,很容易就想到二分法。

但是本题整个数组并不是完全有序的,而是被“旋转”拆分成了两个部分。

如果我们能找到那个旋转点的话,在两个有序的部分进行二分查找就非常轻松了。

那么如果直接遍历数组去找旋转点的话,时间复杂度还是会上升到O(N),不符合题目要求。

我们能不能也用二分去寻找这个旋转点呢? 答案是可以的。

eg 4 5 6 7 1 2 3——旋转点在 1 处
我们以arr[0],也就是4为基准,用mid 去跟 arr[0]比较,如果mid>arr[0],说明旋转点在mid右边,如果mid<arr[0],那么可能当前就是旋转点,或者旋转点在右边。

这也算是满足二段性的一个例子了——二分法并不是一定要有序的时候才能用,满足二段性时,也可以使用。


//查找旋转点 
public static int findRotationPointIndex(int[] arr) {
    // 如果数组长度为2,直接返回较小元素的索引
    if (arr.length == 2) return arr[0] < arr[1] ? 0 : 1;

    // 初始化左右边界
    int left = 0;
    int right = arr.length - 1;

    // 二分查找旋转点
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] >= arr[0]) {
            left = mid + 1;  // mid处于前半段递增序列,旋转点在右半段
        } else {
            right = mid;     // mid处于后半段递增序列或是旋转点
        }
    }
    return left;  // left指向旋转点的索引
}

查找到旋转点之后,我们再在两端进行二分查找就比较容易了。

public int search(int[] arr, int target) {
        // 如果数组长度为1,直接比较目标值与数组唯一元素
        if (arr.length == 1) return target == arr[0] ? 0 : -1;

        // 找到旋转点的索引
        int index = findRotationPointIndex(arr);

        // 初始化左右边界
        int left = 0;
        int right = arr.length - 1;

        // 确定二分查找的范围
        if (index == 0) {
            // 数组没有旋转,直接在整个数组上执行二分查找
            return binaryFind(arr, left, right, target);
        }

        if (target >= arr[0]) {
            right = index; // 目标值可能在旋转点之前(包括旋转点)
        } else {
            left = index; // 目标值在旋转点之后
        }

        // 在确定的范围内执行二分查找
        return binaryFind(arr, left, right, target);
    }
    //二分查找
    public static int binaryFind(int[] arr, int left, int right, int target) {
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }

在这里插入图片描述


6.LeetCode 475 供暖器

在这里插入图片描述

  • 解法

核心思路就是两句话:
(1)对于每个房屋,要么用前面的暖气,要么用后面的,二者取近的,得到距离;
(2)对于所有的房屋,选择最大的上述距离。

所以我们可以将heaters排好序,然后对每个房屋利用二分法去搜索最近的(相邻的)供暖器即可。

代码实现:

public int findRadius(int[] houses, int[] heaters) {
        // 首先对加热器的位置数组进行排序
        Arrays.sort(heaters);
        // 初始化答案为0
        int ans = 0;
        int n = houses.length;
        // 遍历房屋的位置数组
        for (int i = 0; i < n; i++) {
            // 对于每个房屋位置,调用二分查找函数找到其最近的加热器,并更新答案
            ans = Math.max(binarySearch(heaters, houses[i]), ans);
        }
        // 返回最大半径
        return ans;
    }
    
    // 二分查找函数,用于找到距离目标最近的加热器
    public int binarySearch(int[] nums, int target) {
        int n = nums.length;
        // 如果目标大于等于加热器数组中最后一个加热器的位置,直接返回目标与最后一个加热器位置的距离差
        if (target >= nums[n - 1]) return target - nums[n - 1];
        // 如果目标小于等于加热器数组中第一个加热器的位置,直接返回第一个加热器位置与目标的距离差
        if (target <= nums[0]) return nums[0] - target;
        
        // 初始化左右边界
        int l = 0, r = n - 1;
        // 开始二分查找
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) {
                return 0; // 如果找到目标位置,返回距离差为0
            } else if (nums[mid] < target) {
                l = mid + 1; // 如果目标在当前中间值的右侧,更新左边界
            } else {
                r = mid - 1; // 如果目标在当前中间值的左侧,更新右边界
            }
        }
        // 循环结束时,l指向的位置即为最终找到的最近的那个比他大的加热器的位置
        //取两个差值的最小值
        return Math.min(nums[l] - target, target - nums[l - 1]);
    }

7.LeetCode 287 寻找重复数

在这里插入图片描述

  • 这道题博主咋也想不出来能用二分法哈哈哈哈,想破脑壳也找不到二段性

  • 但是 还有有二段性滴~ 哈哈哈哈 刚开始看不太明白没关系 博主也琢磨了好一会儿 差点放弃了…

  • 自己在纸上找一些例子画一画 慢慢就能get到这个点啦

在这里插入图片描述

  • 代码实现
// 寻找重复元素的方法,输入是一个整数数组 nums
public int findDuplicate(int[] nums) {
    int n = nums.length; // 数组的长度
    int l = 1, r = n - 1; // 设定搜索范围,因为题目给出了1到n-1之间的数字重复,所以左边界为1,右边界为n-1
    int ans = -1; // 初始化答案为-1,因为题目保证了一定存在重复元素,因此初始值不影响结果

    // 开始二分搜索
    while (l <= r) {
        int mid = l + (r - l) / 2; // 计算中间值

        int cnt = 0; // 统计小于等于mid的元素个数
        for (int num : nums) {
            if (num <= mid) {
                cnt++;
            }
        }

        // 如果小于等于mid的元素个数(cnt)小于等于mid本身,则重复元素在[mid+1, r]范围内
        if (cnt <= mid) {
            l = mid + 1; // 更新左边界,缩小搜索范围到[mid+1, r]
        } else {
            r = mid - 1; // 否则重复元素在[l, mid-1]范围内
            ans = mid; // 更新答案为当前的mid,因为mid可能是重复的数字
        }
    }

    return ans; // 返回找到的重复元素
}

博主在这篇博客里面会继续更新二分查找的算法题哦~

欢迎点赞关注收藏~

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

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

相关文章

44 - 50题高级字符串函数 / 正则表达式 / 子句 - 高频 SQL 50 题基础版

目录 1. 相关知识点2.例子2.44 - 修复表中的名字2.45 - 患某种疾病的患者2.46 - 删除重复的电子邮箱2.47 - 第二高的薪水2.48 - 按日期分组销售产品2.49 - 列出指定时间段内所有的下单产品2.50 - 查找拥有有效邮箱的用户 1. 相关知识点 相关函数 函数含义concat()字符串拼接upp…

[AIGC] Shell脚本在工作中的常用用法

Shell脚本是一种为 shell 编写的脚本程序。商业上的 Unix Shell 一般都配备图形界面&#xff0c;主要包括&#xff1a;Bourne Shell&#xff08;/usr/bin/sh或/bin/sh&#xff09;、Bourne Again Shell&#xff08;/bin/bash&#xff09;、C Shell&#xff08;/usr/bin/csh&…

专题三:Spring源码中新建module

前面我们构建好了Spring源码&#xff0c;接下来肯定迫不及待来调试啦&#xff0c;来一起看看大名鼎鼎ApplicationContext 新建模块 1、基础步骤 1.1 自定义模块名称如&#xff1a;spring-self 1.2 选择构建工具因为spring使用的是gradle&#xff0c;所以这边需要我们切换默认…

KV260视觉AI套件--PYNQ-DPU

目录 1. 简介 2. DPU 原理介绍 2.1 基本原理 2.2 增强型用法 3. DPU 开发流程 3.1 添加 DPU IP 3.2 在 BD 中调用 3.3 配置 DPU 参数 3.4 DPU 与 Zynq MPSoC互联 3.5 分配地址 3.6 生成 Bitstream 3.7 生成 BOOT.BIN 4. 总结 1. 简介 在《Vitis AI 环境搭建 &…

爬虫中如何创建Beautiful Soup 类的对象

在使用 lxml 库解析网页数据时&#xff0c;每次都需要编写和测试 XPath 的路径表达式&#xff0c;显得非常 烦琐。为了解决这个问题&#xff0c; Python 还提供了 Beautiful Soup 库提取 HTML 文档或 XML 文档的 节点。 Beautiful Soup 使用起来很便捷&#xff0c;…

数据结构-期末复习题

数据结构-期末复习题 一、选择题 1、在数据结构中&#xff0c;与所使用的计算机无关的是数据的&#xff08; ) 结构。 A. 存储B. 物理C. 逻辑D. 物理和存储 【答案】C 【解析】暂无解析2、算法分析的两个主要方面是 ( )。 A. 正确性和简单性B. 可读性和文档性C. 空间复杂度…

拼多多滑块逆向

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi …

onInterceptTouchEvent() 与 onTouch() 事件分析

前言 本文主要分析 onTouch() 与 onTouchEvent() 事件的差异 正文 先看布局文件&#xff1a; <?xml version"1.0" encoding"utf-8"?> <com.longzhiye.intercepttouch.MyFrameLayout xmlns:android"http://schemas.android.com/apk/res…

Big Data Tools插件

一些介绍 在Jetbrains的产品中&#xff0c;均可以安装插件&#xff0c;其中&#xff1a;Big Data Tools插件可以帮助我们方便的操作HDFS&#xff0c;比如 IntelliJ IDEA&#xff08;Java IDE&#xff09; PyCharm&#xff08;Python IDE&#xff09; DataGrip&#xff08;SQL …

ctfshow-web入门-命令执行(web71-web74)

目录 1、web71 2、web72 3、web73 4、web74 1、web71 像上一题那样扫描但是输出全是问号 查看提示&#xff1a;我们可以结合 exit() 函数执行php代码让后面的匹配缓冲区不执行直接退出。 payload&#xff1a; cvar_export(scandir(/));exit(); 同理读取 flag.txt cinclud…

mybatis实现动态sql

第一章、动态SQL MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验&#xff0c;你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特…

网安小贴士(2)OSI七层模型

一、前言 OSI七层模型是一种网络协议参考模型&#xff0c;用于描述计算机网络体系结构中的不同层次和功能。它由国际标准化组织 (ISO) 在1984年开发并发布。 二、定义 OSI七层模型&#xff0c;全称为开放式系统互联通信参考模型&#xff08;Open Systems Interconnection Refe…

微型导轨:手术机器人的高精度“骨骼”

微型导轨精度高&#xff0c;摩擦系数小&#xff0c;自重轻&#xff0c;结构紧凑&#xff0c;被广泛应用在医疗器械中&#xff0c;尤其是在手术机器人中的应用&#xff0c;通过手术机器人&#xff0c;外科医生可以远离手术台操纵机器人进行手术。可以说&#xff0c;是当之无愧的…

6-linux操作文件与数据上传

目录 通配符学习 创建和上传下载文件 系统盘与数据盘 创建和查找文件夹 文件上传与移动 Windows上传文件到服务器方法1 Windows上传文件到服务器方法2&#xff1a;网盘等 删除文件 rm 复制文件cp 移动文件 常用的命令 cp:复制文件和目录。 mv:移动或重命名文件和目…

「漏洞复现」时空智友ERP系统updater.uploadStudioFile 任意文件上传漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

九浅一深Jemalloc5.3.0 -- ①浅*编译调试

目前市面上有不少分析Jemalloc老版本的博文&#xff0c;但5.3.0却少之又少。而且5.3.0的架构与之前的版本也有较大不同&#xff0c;本着“与时俱进”、“由浅入深”的宗旨&#xff0c;我将逐步分析Jemalloc5.3.0的实现。5.3.0的特性请见Releases jemalloc/jemalloc GitHub 另…

Chrome备份数据

Chrome备份数据 1、 导出谷歌浏览器里的历史记录 参考&#xff1a;https://blog.csdn.net/qq_32824605/article/details/127504219 在资源管理器中找到History文件&#xff0c;文件路径&#xff1a; C:\Users\你的电脑用户名\AppData\Local\Google\Chrome\User Data\Default …

2024年06月CCF-GESP编程能力等级认证Scratch图形化编程三级真题解析

本文收录于《Scratch等级认证CCF-GESP图形化真题解析》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(共 15 题,每题 2 分,共 30 分) 第1题 小杨父母带他到某培训机构给他报名参加 CCF 组织的 GESP 认证考试的第 1 级,那他可以选择的认证语言有几…

前端工程化09-webpack静态的模块化打包工具(未完结)

9.1、开发模式的进化历史 webpacks是一个非常非常的强大的一个工具&#xff0c;相应的这个东西的学习也是有一定的难度的&#xff0c;里边的东西非常的多&#xff0c;里面涉及到的 概念的话也是非常非常的多的。 这个东西既然非常重要&#xff0c;那么在我们前端到底处于怎样…

如何在面试中找到最优秀的候选人:雇佣问题的随机化算法探索

目录 一、雇佣问题 二、雇佣问题的本质 三、引入随机化算法的思考过程 &#xff08;一&#xff09;随机排列 &#xff08;二&#xff09;预面试期 &#xff08;三&#xff09;正式雇佣期 四、概率分析 &#xff08;一&#xff09;预面试期的选择 &#xff08;二&#…