【LeetCode 算法专题突破】---二分查找(⭐⭐⭐)

前言

我在算法题目的海洋中畅游已久,也曾在算法竞赛中荣获佳绩。然而,我发现自己对于算法的学习,还缺乏一个系统性的总结和归类。尽管我已经涉猎过不少算法类型,但心中仍旧觉得有所欠缺,未能形成完整的算法体系。

因此,我决定踏上这次算法之旅,对常见的算法进行一次全面的梳理与归类。我希望通过这个过程,能够更深入地理解每个经典算法类型的核心知识,加强我的算法能力,并完善自己的算法体系。

同时,我也希望能够将这次学习的成果与你分享,希望对你也有所帮助。让我们一同在算法的世界里探索、成长,共同迎接未来的挑战吧!

1.经典的不能在经典的二分查找(难度⭐)

Leetcode链接:704. 二分查找

1.1题目描述:

     这是一道非常典型的二分查找算法题目,可以说所有要学习二分查找算法的人都必须掌握这道题。题目要求很简单,就是在一个有序数组中查找目标值target。这道题是二分查找算法的入门题和模版题,没有掌握这道题就无法开始学习更复杂的二分查找算法题目。所以这道题对于二分查找算法的理解和掌握非常关键和必要。

1.2代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        int mid;
        while(left <= right)
        {
            mid = (left + right)/2;
            if(nums[mid]>target)
            {
                right = mid - 1;
            }
            else if(nums[mid]<target)
            {
                left = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
};

通过这道二分查找的典型题,我对二分查找算法的理解更进一步:

我对二分查找的区间划分有了更深的理解:

  • 当mid位置的值小于target时,证明左区间[left, mid-1]都不可能存在target,所以左区间变为[mid+1, right]。
  • 当mid位置的值大于target时,证明右区间[mid+1, right]都不可能存在target,所以右区间变为[left, mid-1]。
  • 当mid位置的值等于target时,就找到了目标值。

我理解到二分查找的本质就是通过比较中间mid值与target的值,可以排除一半的搜索区间,使搜索范围越来越小,直到找到target。

通过这道题我对二分查找算法模板有了更加熟练的掌握,可以应用到其他二分查找题目中去。

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

Leetcode链接:34. 在排序数组中查找元素的第一个和最后一个位置

2.1题目描述:

2.2代码:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1, begin = -1, end = -1, mid;
        //找到区间左边界
        while(left<=right)
        {
            mid = (left + right)/2;
            if(nums[mid] > target)
            {
                right = mid - 1;
            }
            else if(nums[mid] < target)
            {
                left = mid + 1;
            }
            else
            {
                begin = mid;
                right--;//right区间左移,使得mid左移,直到到达左区间边界,此时right正好和left重合
            }
        }
 
        left = 0, right = nums.size() - 1;
 
        //找到区间有边界
        while(left<=right)
        {
                        mid = (left + right)/2;
            if(nums[mid] > target)
            {
                right = mid - 1;
            }
            else if(nums[mid] < target)
            {
                left = mid + 1;
            }
            else
            {
                end = mid;
                left++;//left区间右移,使得mid右移,直到到达又区间边界,此时left正好和right重合
            }
        }
        return {begin,end};
    }
};

这道题的大思路和上面的题并没有太大区别,只是为了找到左右区间,需要两次二分查找

具体来说,在求左右区间边界时,处理mid==target的情况需要进行特别考虑:

  • 求左区间边界时,需要在记录begin索引后,继续将right索引左移,使得mid向左逼近,直到不等于target时才能锁定左区间边界。
  • 求右区间边界时,需要在记录end索引后,继续将left索引右移,使得mid向右逼近,直到不等于target时才能锁定右区间边界。

这种细微的逻辑调整体现了二分查找的灵活性,在保持模板框架不变的情况下,通过简单逻辑修改就可应对新的问题。

3. 有效的完全平方数(难度⭐)

Leetcode链接:367. 有效的完全平方数

3.1题目描述:

撇开具体问题不讨论,假设需要找到完全平方数,你可能会采用这样的方法:将原数进行二分,然后将得到的中间值平方,观察是否等于目标值。如果相等,那么找到了完全平方数;如果不等,就可以根据大小关系缩小搜索范围,继续二分。为什么选择这种方式呢?因为这种方法的效率相对较高。既然如此,我们为何不考虑运用二分法来解决这类问题呢?

3.2代码:

class Solution {
public:
    bool isPerfectSquare(int num) {
        long long left = 0, right = num, mid = 0;
        long long s;
        while(left<=right)
        {
            mid = (left + right)/2;
            s = mid * mid;
            if(s>num)
            {
                right=mid-1;
            }
            else if(s<num)
            {
                left=mid+1;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
};

在这个解决方案中,我们并没有深入探讨具体的代码细节,只是简单地将二分算法应用于这个场景。然而,通过这个简单的经验,我们可以得出一个更加广泛的启示:如果今后遇到类似的问题,我们可以考虑运用二分法。

4.寻找峰值(难度⭐⭐)

二分熟练度up up up~

Leetcode链接:162. 寻找峰值

4.1题目描述:

在面对这个问题时,我们甚至没有提供目标值(target),而且输入序列并不保证是有序的。这是否意味着我们可以应用二分查找算法呢?
让我们尝试通过代码来展示这一思路,看看是否能够在这样的条件下成功运用二分查找的思想解决问题。

4.2代码:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left = 0, right = nums.size() - 1, mid = 0;
        while(left<right)
        {
            mid = (left + right)/2;
            if(nums[mid]>nums[mid + 1])
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        return right;
    }
};

初看之下,面对问题好像难以着手,但让我们仔细分析一下,看看能否找到解决方案。

题目要求在数组中找到任意一个峰值,那么我们可以考虑将数组分为两个区间:一个是递增区间,另一个是递减区间。峰值实际上是这两个区间的交界点。

假设我们随机选择一个点进行比较,如果它比右侧位置的值小,说明它在一个递增的区间;反之,如果它比右侧位置的值大,说明它在一个递减的区间。

基于这个性质,我们可以运用二分算法。当 nums[mid] < nums[mid+1] 时,表示在一个递增区间,峰值必定在 mid+1 及其之后的位置;而当 nums[mid] > nums[mid+1] 时,表示在一个递减区间,峰值可能在 mid 或其之前的位置(需要注意,峰值有可能就是在 mid 位置)。

因此,我们更新 right = mid,而不需要进行 -1 的操作。由于不需要 -1,当 left == right 时,如果已经找到峰值,我们应该如何退出循环呢?

这里可以进行特殊处理,或者将循环条件改成 left < right。在某些情况下,模板并不是固定不变的,我们可以根据实际情况进行调整。通过解决这道题,我们不仅学到了一些技巧,也积累了宝贵的经验。

5.寻找旋转排序数组中的最小值(难度⭐⭐)

Leetcode链接:153. 寻找旋转排序数组中的最小值

5.1题目描述:

这题的解题思路与前面一道问题相似,我们需要根据所给的条件找到一个可以进行比较的参照物或者说参照系。让我们来审视一下具体的代码。

5.2代码:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size() - 1;
        int left = 0, right = n, mid = 0;
        while(left < right)
        {
            mid = (left + right)/2;
            if(nums[mid] > nums[n])
            {
                left = mid + 1;
            }
            else if(nums[mid] < nums[n])
            {
                right = mid;
            }
        }
        return nums[left];
    }
};

考虑到题目给出的是一个经过旋转的升序数组,这使得数组不再是有序的,我们需要思考如何运用二分法来解决这一问题。

观察到,经过旋转的升序数组可以分为两个递增的区间,一个较大的区间和一个较小的区间。我们可以以区间的最大值作为参照物来进行分析。

以最右边的元素为例,它可能是小区间的最大值,也可能是大区间的最大值。如果它是大区间的最大值,这就意味着数组没有经过旋转,因此我们可以先忽略这种特殊情况(这是一个解题的小技巧,特殊情况可以后续再处理)。

题目要求找到最小的元素,即小区间的最小值。因此,我们需要找到小区间,并在其中找到最小元素。具体操作如下:

  • 当 nums[mid] > nums[n] 时,表示中间位置 mid 处于大区间,因此将 left 调整为 mid + 1;
  • 当 nums[mid] < nums[n] 时,表示中间位置 mid 处于小区间,因此将 right 调整为 mid。注意,这里不能减1,因为我们要找的值在小区间内,不能排除掉中间元素。
  • 接着,考虑特殊情况,即当 nums[mid] < nums[n] 时,数组的最右元素 n 是大区间的最大值。如果我们让 right = mid,会导致最终循环结束时出现 left = right 的情况。为了避免这种情况,我们将循环的条件调整为 left < right。

这道题是二分法的一个变式,关键在于找到以何为参照系来确定区间的位置,一旦确定,后续的工作就变得相对容易。

6.点名(难度⭐)

Leetcode链接:LCR 173. 点名

6.1题目描述:

这道题我其实一开始也想不出来,选这道题的目的其实也是想说明,算法积累的重要性,见多识广,才能思路开阔,临危不乱。

6.2代码:

class Solution 
{
public:
    int takeAttendance(vector<int>& records) 
    {
        int n = records.size() - 1;
        int left = 0, right = n, mid = 0;
        while(left < right)
        {
            mid = (left + right)/2;
            if(records[mid] == mid)
            {
                left = mid + 1;
            }
            else
            {
                right = mid;
            }
        }
        if (records[right] == right) 
        {
            return right+1;
        }
        return right;
 
    }
};

总结一下:

完成了这六道题目后,相信你对二分查找已经得心应手了~

接下来,继续努力,迎接新的挑战吧~

如果有一天觉得对二分有些生疏了,不妨回来再刷一遍,巩固一下技能~

最后,祝愿你未来的刷题之路愉快顺利~

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

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

相关文章

微服务超大Excel文件导出方案优化

1、在导出Excel时经常会碰到文件过大&#xff0c;导出特别慢 2、微服务限制了请求超时时间&#xff0c;文件过大情况必然超时 优化思路&#xff1a; 1、文件过大时通过文件拆分、打包压缩zip&#xff0c;然后上传到oss,并设置有效期&#xff08;30天过期&#xff09; 2、把…

gitte上传项目操作

一、项目背景 打比赛&#xff0c;多个人合作&#xff0c;选择github&#xff0c;顺便了解下git的代码操作。 二、步骤 2.1 新建仓库 2.2 打开你要上传到库的项目 2.2 选择 Git Bash Here 输入指令 git init 2.3 查找github的仓库 2.2 将文件放入暂缓区 git add . 2.3填写…

【MySQL 系列】MySQL 语句篇_DML 语句

DML&#xff08;Data Manipulation Language&#xff09;&#xff0c;即数据操作语言&#xff0c;用于操作数据库对象中所包含的数据。常用关键字包括&#xff1a;插入&#xff08;INSERT&#xff09;、更新&#xff08;UPDATE&#xff09;、删除&#xff08;DELETE&#xff09…

第一个 Angular 项目 - 添加路由

第一个 Angular 项目 - 添加路由 前置项目是 第一个 Angular 项目 - 添加服务&#xff0c;之前的切换页面使用的是 ngIf 对渲染的组件进行判断&#xff0c;从而完成渲染。这一步的打算是添加路由&#xff0c;同时添加 edit recipe 的功能(同样通过路由实现) 用到的内容为&…

快速上手:使用Hexo搭建并自定义个人博客

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

UUU.咕咚视频批量混剪软件下载,批量剪辑个体无限批量生成,批量合成视频,批量混剪视频的软件是什么样的?批量剪辑软件在哪下载?批量混剪软件下载容易吗?

[md]# 前言 工具的产生源于dy出的火山引擎的云视频混剪制作是按分钟数收费的&#xff0c;这个软件既能实现正常混剪也能避免二次收费。属于FFMPEG合成的。 欢迎大家给一些好的建议和功能&#xff0c;回复可见&#xff0c;附加了一些天卡&#xff0c;周卡&#xff0c;请大家不要…

Milvus 向量数据库实践 - 1

假定你已经安装了docker、docker-compose 环境 参考的文档如下&#xff1a; Milvus技术探究 - 知乎 MilvusClient() - Pymilvus v2.3.x for Milvus 一文带你入门向量数据库milvus 一、在docker上安装单机模式milvus数据库 1、 进入milvus官网&#xff1a; Install Milvus Stand…

【linuxC语言】dup、dup2函数

文章目录 前言一、dup函数二、dup2函数三、将标准输出重定向到文件总结 前言 在Linux环境下&#xff0c;dup、dup2以及原子操作都是用于文件描述符管理和处理的重要工具。这些功能提供了对文件描述符进行复制和原子操作的能力&#xff0c;使得在多线程或多进程环境中更加安全和…

Randoop 报错 Cannot find the Java compiler 的解决方案

编写测试用例是一项困难且耗时的工作&#xff0c;但同时又是好的软件工程的重要部分。Randoop是一个随机测试的测试用例生成的工具&#xff0c;能够自动的为Java代码中的类生成单元测试。 官网链接&#xff1a; https://randoop.github.io/randoop/manual/index.html。 正确的…

新手小白剪辑视频知识点:视频分辨率和位深度,有什么区别?

新手小白需要了解的视频剪辑知识点&#xff0c;什么是视频分辨率尺寸(文件大小)和位深度&#xff1f; 分辨率尺寸/文件大小 常见的视频分辨率是高清和 4K。高清素材的屏幕像素&#xff08;宽度 x 高度&#xff09;测量值通常为 1920 x 1080&#xff0c;而 4K 素材是其四倍&am…

【Linux通识】之端口到底是个什么?

端口的本质是计算机上的一小块内存&#xff01; 端口的概念 在Linux系统&#xff08;以及其他所有支持网络通信的操作系统&#xff09;中&#xff0c;端口&#xff08;Port&#xff09;可以理解为计算机在网络上与外界沟通的虚拟通道或门牌号码。当我们谈论网络通信时&#xf…

【教程】Github环境配置新手指南(超详细)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 文章目录 一、Github初始设置&#xff08;一&#xff09;登入Github&#xff08;二&#xff09;新建仓库 二、本地Git配置&am…

在Leaflet中使用Turf.js生成范围多边形的两种实现方式

目录 前言 一、场景需求 1、Leaflet.js的不足 2、Turf.js 二、原始数据展示 1、点位数据展示 2、定义样式 3、定位数据初始化 三、Turfjs中bbox生成 1、官网讲解 2、轨迹bbox生成 四、Turfjs生成外包多边形 1、官网例子 2、凸多边形生成 总结 前言 在一些共享出…

机器学习--循环神经网路(RNN)2

在这篇文章中&#xff0c;我们介绍一下其他的RNN。 一.深层RNN 循环神经网络的架构是可以任意设计的&#xff0c;之前提到的 RNN 只有一个隐藏层&#xff0c;但 RNN 也可以是深层的。比如把 xt 丢进去之后&#xff0c;它可以通过一个隐藏层&#xff0c;再通过第二个隐藏层&am…

三  超级数据查看器   讲解稿    主界面和系统功能介绍

三 超级数据查看器 讲解稿 主界面和系统功能介绍 APP百度下载地址 下载地址4 ​讲解稿全文: 大家好。 今天我们讲解一下&#xff0c;超级数据查看器主界面。 首先&#xff0c;我们打开超级数据查看器。 打开之后&#xff0c;进入的第一个界面就是主界面。这个页面由三…

网络编程---网络编程入门、UDP通信程序、TCP通信程序

1.网络编程入门 1.网络编程概述 网络编程&#xff1a; 在网络通信协议下&#xff0c;实现网络互连的不同计算机上运行的程序间可以进行数据传输 计算机网络&#xff1a; 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#…

docker安装ES、LogStash、Kibana

文章目录 一、安装Elasticsearch1. 安装Elasticsearch2. 安装IK分词器3. elasticsearch-head 监控的插件4. 配置跨域 二、安装LogStash三、安装kibana四、SpringBoot集成LogStash&#xff0c;将日志输出到ES中五、 启动项目&#xff0c;监控项目运行 提示&#xff1a;以下是本篇…

ABAP接口部分-C#调用RFC

目录 ABAP接口部分-C#调用RFC创建表结构创建RFC函数创建C#项目引用SAP .Net Connector包绘制窗口的控件最终布局代码 项目配置报错SAP.Middleware.Connector.RfcDestinationManager报错SAP.Middleware.Connector.RfcLoginexception报错SAP.Middleware.Connector.RfcInvalidStat…

Docker基础教程 - 8 镜像仓库

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 8 镜像仓库 在安装 Docker 的时候&#xff0c;默认使用的是 DockerHub&#xff0c;后来为了提升速度&#xff0c;配置的镜像仓库是使用阿里云的镜像仓库&#xff0c;拉取的是别人制作的镜像&…

第五十三回 入云龙斗法破高廉 黑旋风下井救柴进-AI训练数据处理和读取

罗真人教了公孙胜五雷天罡正法&#xff0c;并让他记住“逢幽而止&#xff0c;遇汴而环”八个字。三人辞别了罗真人&#xff0c;戴宗先回去报信&#xff0c;李逵和公孙胜结伴而行。 走了三天&#xff0c;来到了武冈镇&#xff0c;李逵碰到一个铁匠&#xff0c;叫金钱豹子汤隆&a…