【二分算法】——8个题目让你找到二分算法的感觉势如破竹

文章目录

  • 1.二分查找
  • 2.在排序数组中查找元素的第一个和最后一个位置
  • 3.x的平方根
  • 4.搜索插入位置
  • 5.山脉数组的峰顶索引
  • 6.寻找峰值
  • 7.寻找旋转排序数组中的最小值
  • 8.JZ53(2)

1.二分查找

https://leetcode.cn/problems/binary-search/
在这里插入图片描述

思路: 标准的二分查找。给定一个有序数组和目标值,每次选择数组的中间元素与目标值比较。如果中间元素等于目标值,则返回该索引;若中间元素小于目标值,则在右侧继续搜索;若中间元素大于目标值,则在左侧继续搜索。时间复杂度为O(log n)。

步骤: 初始化: 设置 left 和 right 分别为数组的开头和结尾。
二分查找: 计算中点 mid。
如果 nums[mid]等于目标值,返回 mid。
如果 nums[mid] 小于目标值,更新 left = mid + 1。
如果 nums[mid]大于目标值,更新 right = mid - 1。 终止条件: 如果没有找到,返回 -1。

class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        int n = nums.size(); // 获取数组的大小
        // 定义左右边界,left 是左边界,right 是右边界
        for(int left = 0,right = n - 1;left <= right;) 
        {
            // 计算中间索引 mid,避免溢出的安全写法可以使用:mid = left + (right - left) / 2;
            int mid = (left + right) / 2;
            // 如果中间值小于目标值,说明目标值在右半部分,移动左边界到 mid + 1
            if(nums[mid] < target) left = mid + 1;
            // 如果中间值大于目标值,说明目标值在左半部分,移动右边界到 mid - 1
            if(nums[mid] > target) right = mid - 1;
            // 如果中间值等于目标值,返回当前中间值的索引
            if(nums[mid] == target) return mid;
        }
        // 如果没有找到目标值,返回 -1
        return -1;
    }
};

2.在排序数组中查找元素的第一个和最后一个位置

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/
在这里插入图片描述
思路: 这是经典的“查找范围”问题,可以用两次二分查找解决。第一次找第一个出现位置,第二次找最后一个出现位置。时间复杂度为O(log n),适合处理排序数组。

步骤:
查找第一个位置: 使用二分查找,找到目标值的第一个位置。
每次判断 nums[mid] 是否大于等于target,是则向左移动,否则向右移动。
查找最后一个位置: 使用二分查找,找到目标值的最后一个位置。
每次判断 nums[mid]是否小于等于 target,是则向右移动,否则向左移动。
返回结果: 如果找到两个位置,返回它们的范围,否则返回 [-1, -1]。

class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        // 如果数组为空,直接返回 {-1, -1}
        if(nums.size() == 0) return {-1, -1};
        
        // 获取数组的大小
        int n = nums.size();
        
        // 初始化左右边界
        int left = 0, right = n - 1;
        
        // 查找左端点,目标是找到 target 第一次出现的位置
        while(left < right)
        {
            // 计算中间索引,防止溢出的安全写法
            int mid = left + (right - left) / 2;
            
            // 如果中间值小于目标值,说明目标值在右侧,将 left 移到 mid + 1
            if(nums[mid] < target) left = mid + 1;
            
            // 如果中间值大于等于目标值,右边界移动到 mid,继续查找
            if(nums[mid] >= target) right = mid;
        }
        
        // 如果左边界的值不等于 target,说明数组中没有该值,返回 {-1, -1}
        if(nums[left] != target) return {-1, -1};
        
        // 记录找到的目标值的起始位置
        int begin = left;
        
        // 查找右端点,目标是找到 target 最后一次出现的位置
        right = n - 1;  // 重新初始化右边界
        
        // 使用二分查找找右边界
        while(left < right)
        {
            // 计算中间索引,为了确保 mid 偏向右侧,加 1
            int mid = left + (right - left + 1) / 2;
            
            // 如果中间值小于等于目标值,说明右边界可能在右侧,将 left 移动到 mid
            if(nums[mid] <= target) left = mid;
            
            // 如果中间值大于目标值,说明右边界在左侧,将 right 移到 mid - 1
            if(nums[mid] > target) right = mid - 1;
        }
        
        // 返回目标值的起始和结束位置
        return {begin, right};
    }
};

3.x的平方根

https://leetcode.cn/problems/sqrtx/
在这里插入图片描述
思路: 使用二分查找法求x的平方根。定义初始范围为0,x,每次取中间值m,计算m²与x的比较。如果m²小于x,则移动左边界;如果m²大于x,则移动右边界。直至找到平方根或其整数部分。

步骤:
初始化: 设置 left 为 0,right 为 x。
二分查找: 计算中点 mid。
如果 mid * mid 小于x,说明平方根在右侧,更新 left = mid + 1。
如果 mid * mid 大于 x,说明平方根在左侧,更新 right =mid - 1。
终止条件: 当 left 和 right 相遇时,取较小值作为结果的整数部分。

class Solution 
{
public:
    int mySqrt(int x) 
    {
        // 定义二分查找的左右边界
        int left = 1, right = x;
        
        // 如果 x 小于 1,直接返回 0
        if(x < 1) return 0;
        
        // 开始二分查找
        while(left < right)
        {
            // 计算中间值,为了避免溢出,使用 left + (right - left + 1) / 2
            long long mid = left + (right - left + 1) / 2;
            
            // 如果 mid 的平方小于等于 x,说明 mid 可能是答案,或者答案在右侧,将 left 移动到 mid
            if(mid * mid <= x) left = mid;
            // 如果 mid 的平方大于 x,说明 mid 太大,答案在左侧,将 right 移动到 mid - 1
            else if(mid * mid > x) right = mid - 1;
        }
        
        // 返回找到的平方根的整数部分
        return left;
    }
};

4.搜索插入位置

https://leetcode.cn/problems/search-insert-position/description/
在这里插入图片描述
思路: 给定一个有序数组和目标值,要求返回目标值在数组中的插入位置。如果数组中存在目标值,返回其索引;若不存在,返回其应该插入的位置。使用二分查找,找到第一个大于或等于目标值的位置。

步骤:
初始化: 使用 left 和 right 指针。
二分查找: 计算中点 mid。
如果 nums[mid] 大于或等于目标值target,则将 right 更新为 mid - 1。
否则,将 left 更新为 mid + 1。
终止条件: 当 left 和right 相遇时,left 即为目标值要插入的位置。

class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        int n = nums.size(); // 获取数组的大小
        // 定义左右边界,left 是左边界,right 是右边界
        for(int left = 0,right = n - 1;left <= right;) 
        {
            // 计算中间索引 mid,避免溢出的安全写法可以使用:mid = left + (right - left) / 2;
            int mid = (left + right) / 2;
            // 如果中间值小于目标值,说明目标值在右半部分,移动左边界到 mid + 1
            if(nums[mid] < target) left = mid + 1;
            // 如果中间值大于目标值,说明目标值在左半部分,移动右边界到 mid - 1
            if(nums[mid] > target) right = mid - 1;
            // 如果中间值等于目标值,返回当前中间值的索引
            if(nums[mid] == target) return mid;
        }
        // 如果没有找到目标值,返回 -1
        return -1;
    }
};

5.山脉数组的峰顶索引

https://leetcode.cn/problems/peak-index-in-a-mountain-array/
在这里插入图片描述

思路: 山脉数组是先递增后递减的数组,因此其峰顶就是数组中最大值的位置。可以使用二分查找,类似“寻找峰值”的思路,找到这个峰顶索引。

步骤:
初始化: 使用 left 和 right 指针,分别指向数组的开头和结尾。
二分查找: 计算中点 mid。 如果 nums[mid]大于 nums[mid + 1],峰顶在左侧,更新 right = mid。
如果 nums[mid] 小于 nums[mid + 1],峰顶在右侧,更新 left = mid + 1。
终止条件: 当 left 和 right 相遇时,left 即为峰顶的索引。

class Solution 
{
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        // 获取数组大小
        int n = arr.size();
        
        // 定义左右边界。由于数组两端不能是峰值,山峰一定在1到n-2之间
        int left = 1, right = n - 2;
        
        // 当左边界小于右边界时,继续查找
        while(left < right)
        {
            // 计算中间位置,使用 left + (right - left + 1) / 2 可以避免溢出
            int mid = left + (right - left + 1) / 2;
            
            // 如果中间元素比左侧的前一个元素小,说明山峰在左边,右边界移动到 mid - 1
            if(arr[mid] < arr[mid - 1]) right = mid - 1;
            // 否则,山峰在右侧或当前位置,左边界移动到 mid
            else left = mid;
        }
        
        // 循环结束时,left 和 right 会指向同一个位置,这就是峰值的索引
        return left;
    }
};

6.寻找峰值

https://leetcode.cn/problems/find-peak-element/description/
在这里插入图片描述
思路: 峰值定义为该元素比相邻元素大。可以使用二分查找的变种。每次选择中点,如果中点比其右侧元素小,则峰值在右侧;如果中点比其右侧元素大,则峰值在左侧。这样逐步缩小搜索范围,直至找到峰值。

步骤:
初始化: 定义 left 和 right 指针。
二分查找: 每次计算中点 mid。 如果 nums[mid] 大于 nums[mid + 1],说明峰值在左侧或就是 mid,更新 right = mid。
如果 nums[mid] 小于 nums[mid + 1],说明峰值在右侧,更新 left = mid + 1。
终止条件: 当 left 和 right 相遇时,left 即为峰值所在的位置。

class Solution 
{
public:
    int findPeakElement(vector<int>& nums) 
    {
        // 初始化左右边界
        int left = 0, right = nums.size() - 1;
        
        // 二分查找
        while(left < right)
        {
            // 计算中间索引,避免溢出
            int mid = left + (right - left) / 2;
            
            // 如果中间值大于它右边的元素,说明峰值在左侧或当前元素是峰值,将右边界移到 mid
            if(nums[mid] > nums[mid + 1]) right = mid;
            // 如果中间值小于它右边的元素,说明峰值在右侧,将左边界移到 mid + 1
            else left = mid + 1;
        }
        
        // 返回最终找到的峰值索引
        return left;
    }
};

7.寻找旋转排序数组中的最小值

https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/description/
在这里插入图片描述
思路: 旋转排序数组的特点是其包含两个有序的子数组(前一段大于后一段)。最小值通常出现在这两个有序子数组的交界处。可以使用二分查找,比较中点和右端点的值,若中点大于右端点,最小值在右侧;若中点小于右端点,最小值在左侧。

步骤:
初始化: 定义 left 和 right 指针。
二分查找: 每次计算中点 mid。
如果 nums[mid] 大于 nums[mid + 1],说明峰值在左侧或就是 mid,更新 right = mid。
如果 nums[mid] 小于 nums[mid +1],说明峰值在右侧,更新 left = mid + 1。
终止条件: 当 left 和 right 相遇时,left 即为峰值所在的位置。

class Solution 
{
public:
    int findMin(vector<int>& nums) 
    {
        int n = nums.size();   // 获取数组大小
        int left = 0, right = n - 1;   // 初始化左右边界
        
        // 二分查找
        while(left < right) 
        {
            int mid = left + (right - left) / 2;   // 计算中间索引,避免溢出
            
            // 如果中间值大于最右侧值,说明最小值在右侧,更新 left
            if(nums[mid] > nums[right]) left = mid + 1;
            
            // 否则,最小值在左侧或当前 mid 处
            else right = mid;
        }
        
        // 最小值在 left 处
        return nums[left];
    }
};

8.JZ53(2)

《剑指offer面试题53II》
题目类型: 0〜n-1 中缺失的数字(面试题53 - II)
因为这题作者在leetcode中未找到这题原题,不过有一道类似题目
(这题在leetcode消失的原因是剑指offer在leetcode上下架了)
https://leetcode.cn/problems/que-shi-de-shu-zi-lcof/
在这里插入图片描述

思路: 因为数组是排好序的,可以使用二分查找分别找到目标数字的第一个出现位置和最后一个出现位置。然后计算出最后一次出现位置与第一次出现位置的差值,得到出现的次数。

class Solution {
public:
    // 该函数通过二分查找处理考勤记录,返回某个与索引相关的结果
    int takeAttendance(vector<int>& records) {
        // 初始化左右边界
        int left = 0, right = records.size() - 1;

        // 执行二分查找
        while (left < right) {
            // 计算中间索引,避免溢出
            int mid = left + (right - left) / 2;

            // 如果记录值等于索引值,继续在右侧查找
            if (records[mid] == mid) 
                left = mid + 1;
            // 否则缩小右侧边界
            else 
                right = mid;
        }

        // 如果最终找到的位置的索引值等于该位置的记录值,则返回 left + 1,否则返回 left
        return left == records[left] ? left + 1 : left;
    }
};

此外这题还可以用4种O(n)时间复杂度方法去求
第1种:直接遍历寻找,第2种:哈希,第3种:异或,第4种:等差求和。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【2024版本】Mac/Windows IDEA安装教程

IDEA 2024版本真的很强大&#xff0c;此外JDK发布了最新稳定版 JDK21 &#xff0c;只有新版本支持JDK 21、JDK22。原来数据库插件不支持redis等一些NoSql的数据库的连接&#xff0c;如果要使用需要自己单独装收费的插件。直接打开idea就很吃内存了&#xff0c;再打开其他一大堆…

47 C 语言实战项目——家庭收支记账软件

目录 1 需求说明 1.1 菜单显示 1.2 登记收入 1.3 登记支出 1.4 显示收支明细 1.5 退出 2 流程分析 2.1 总流程图 2.2 登记收入流程图 2.3 登记支出流程图 2.4 收支明细流程图 2.5 退出流程图 3 代码实现 3.1 框架搭建 3.2 收支明细功能 3.3 登记收入功能 3.4 …

23年408数据结构

第一题&#xff1a; 解析&#xff1a; 第一点&#xff0c;我们要知道顺序存储的特点&#xff1a;优点就是随用随取&#xff0c;就是你想要查询第几个元素可以直接查询出来&#xff0c;时间复杂度就是O(1)&#xff0c;缺点就是不适合删除和插入&#xff0c;因为每次删除和插入一…

【数据分享】2000—2023年我国省市县三级逐年植被覆盖度(FVC)数据(Shp/Excel格式)

之前我们分享过2000—2023年逐月植被覆盖度&#xff08;FVC&#xff09;栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;和Excel和Shp格式的省市县三级逐月FVC数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;原始的逐月栅格数据来源于高吉喜学者…

青云AI算力创新:直击AI落地痛点 打造企业数智化基石

在当今这个数字化、智能化的时代&#xff0c;企业数字化转型、智能化升级应用实践在加速&#xff0c;AI算力已经成为企业数字化转型和智能化升级的重要基石&#xff0c;而AI算力在推动技术创新和业务增长中起到了关键作用。青云科技近日举办的AI算力发布会&#xff0c;标志着AI…

知识图谱入门——5:Neo4j Desktop安装和使用手册(小白向:Cypher 查询语言:逐步教程!Neo4j 优缺点分析)

Neo4j简介 Neo4j 是一个基于图结构的 NoSQL 数据库&#xff0c;专门用于存储、查询和管理图形数据。它的核心思想是使用节点、关系和属性来描述数据。图数据库非常适合那些需要处理复杂关系的数据集&#xff0c;如社交网络、推荐系统、知识图谱等领域。 与传统的关系型数据库…

如何快速给word文件加拼音?请跟着步骤完成吧

如何快速给word文件加拼音&#xff1f;在日常工作中&#xff0c;我们时常会遇到需要为Word文件中的文字添加拼音的情况&#xff0c;这尤其在教育、出版或国际交流等领域显得尤为重要。为文字配上拼音&#xff0c;不仅能帮助学习者准确发音&#xff0c;还能提升文档的可读性和普…

【JavaEE初阶】深入理解不同锁的意义,synchronized的加锁过程理解以及CAS的原子性实现(面试经典题);

前言 &#x1f31f;&#x1f31f;本期讲解关于锁的相关知识了解&#xff0c;这里涉及到高频面试题哦~~~ &#x1f308;上期博客在这里&#xff1a;【JavaEE初阶】深入理解线程池的概念以及Java标准库提供的方法参数分析-CSDN博客 &#x1f308;感兴趣的小伙伴看一看小编主页&am…

【STM32单片机_(HAL库)】4-2-1【定时器TIM】定时器输出PWM实现呼吸灯实验

1.硬件 STM32单片机最小系统LED灯模块 2.软件 pwm驱动文件添加定时器HAL驱动层文件添加GPIO常用函数定时器输出PWM配置步骤main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "pwm.h"int main(void) {HA…

【瑞萨RA8D1 CPK开发板】串口的使用和STDOUT输出重定向

串口 本次串口的使用关于时钟导致串口的波特率不对&#xff0c;坑了我很久的时间 使能时钟 串口发现一个问题就是&#xff0c;只能使用下边的时钟配置&#xff0c;修改时钟源和分频系数都会导致串口波特率不正常&#xff0c;这种问题出现在mdkrasc的使用场景之下&#xff1b…

adaptor lora基础

https://www.zhihu.com/question/508658141/answer/3340979311 adaptor和PEFT的区别&#xff1a;前者在模型子层后加一个小型的dense&#xff1b;后者直接稀疏化模型本身&#xff1b; Loading Pre-Trained Adapters — AdapterHub documentation CVPR 2024 | SD-DiT&#xff…

Python | Leetcode Python题解之第468题验证IP地址

题目&#xff1a; 题解&#xff1a; class Solution:def validIPAddress(self, queryIP: str) -> str:if queryIP.find(".") ! -1:# IPv4last -1for i in range(4):cur (len(queryIP) if i 3 else queryIP.find(".", last 1))if cur -1:return &q…

每日OJ题_牛客_小乐乐改数字_模拟_C++_Java

目录 牛客_小乐乐改数字_模拟 题目解析 C代码 Java代码 牛客_小乐乐改数字_模拟 小乐乐改数字_牛客题霸_牛客网 (nowcoder.com) 描述&#xff1a; 小乐乐喜欢数字&#xff0c;尤其喜欢0和1。他现在得到了一个数&#xff0c;想把每位的数变成0或1。如果某一位是奇数&#…

【网络安全】CVE-2024-46990: Directus环回IP过滤器绕过实现SSRF

未经许可,不得转载。 文章目录 背景漏洞详情受影响版本解决方案背景 Directus 是一款开源 CMS,提供强大的内容管理 API,使开发人员能够轻松创建自定义应用程序,凭借其灵活的数据模型和用户友好的界面备受欢迎。然而,Directus 存在一个漏洞,允许攻击者绕过默认的环回 IP …

大数据之——VWare、Ubuntu、CentOs、Hadoop安装配置

前言&#xff1a;这里很抱歉前几期考研专题以及PyTorch这些内容都没有更新&#xff0c;并不是没有在学了&#xff0c;而是事太鸡儿多了&#xff0c;前不久刚刚打完华为开发者比赛&#xff0c;然后有紧接着高数比赛、考研复习&#xff0c;因此这些后续文章都在草稿状态中&#x…

Allegro在PCB上开槽的三种方法操作指导

Allegro如何在PCB上开槽的三种方法操作指导 当PCB有特殊设计要求的时候&#xff0c;需要在PCB上开槽&#xff0c;Allegro支持在PCB上开槽操作&#xff0c;具体操作如下 以下图为例&#xff0c;需要在这个板框中间开槽 开方形槽 选择shape add rect命令 画在Board Geometry-o…

【技术详解】SpringMVC框架全面解析:从入门到精通(SpringMVC)

文章目录 【技术详解】SpringMVC框架全面解析&#xff1a;从入门到精通(SpringMVC)SpringMVC概述1. 三层架构与MVC架构区别1.1 三层架构1.2 MVC架构1.3前后端分离开发模式 2. SpringMVC环境搭建2.1 注解启动方式2.2 xml启动方式2.3 SpringMVC PostMan工具使用 3. SpringMVC 请求…

MySQL 实验1:Windows 环境下 MySQL5.5 安装与配置

MySQL 实验1&#xff1a;Windows 环境下 MySQL5.5 安装与配置 目录 MySQL 实验1&#xff1a;Windows 环境下 MySQL5.5 安装与配置一、MySQL 软件的下载二、安装 MySQL三、配置 MySQL1、配置环境变量2、安装并启动 MySQL 服务3、设置 MySQL 字符集4、为 root 用户设置登录密码 一…

【华为欧拉】国产OpenEuler服务器系统安装以及图形界面

openEuler下载 | openEuler ISO镜像 | openEuler社区官网 下载安装iso 本次选择4G的社区版本 安装&#xff0c;复制到光盘&#xff0c;光盘引导安装。虚拟机安装&#xff0c;准备好iso文件引用&#xff0c;指定好安装源&#xff0c;安装界面和centOS基本一样。选择最小安装就…

代码随想录算法训练营第三十天|491. 非递减子序列,46. 全排列,47. 全排列 II,332. 重新安排行程,51. N 皇后,37. 解数独

491. 非递减子序列&#xff0c;46. 全排列&#xff0c;47. 全排列 II&#xff0c;332. 重新安排行程&#xff0c;51. N 皇后&#xff0c;37. 解数独 491. 非递减子序列46. 全排列47. 全排列 II332. 重新安排行程51. N 皇后37. 解数独 491. 非递减子序列 给你一个整数数组 nums…