leetcode面试经典二分系列刷题心得

闲来无事巩固算法基础,发现自己的二分几乎从来没系统刷过题,基础很是薄弱。

二分法不愧称为新人杀手,刷起来很是吃力,感觉明明学了几套二分模板,但是却不知道如何去运用,很多读者在初次尝试刷二分题时候,想必多数也是深有此体会,力扣的150题面试经典之前我并没有刷过,这次刷来感觉题还不错,写几篇文章好好的体会一下二分的巧妙之处吧!

本文章将按照一定的次序(不一定是力扣次序)讲解面试经典中的二分系列的若干题,它们都各有各自的奥妙,有一些题还是很好的很值得多刷的,除去题解的基本思路讲解外,老规矩我还会添加一些个人的讲解和看法以及对各题的总结。

首先是第一道题

35.搜索插入位置

35. 搜索插入位置 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/search-insert-position/description/?envType=study-plan-v2&envId=top-interview-150这道题题目并不难,我之前也做过,首次没做出来,经过很多次的重复之后现在才能做得很熟练

一开始的题解参考的是官方题解,给出的思路是左闭右闭区间解法。

left=0,right=元素个数-1,为什么叫左闭右闭区间因为是这两左右边界都能取,所以是这样叫的,它的判断条件通常写成left<=right作为循环判断点(也有left<right的写法)。

解题思路初步的想法大体上是这样的,求出以当前左右边界为起始和终止点的中间下标mid=(left+right)/2,然后比较当前数据如果等于target目标值直接返回,如果不等于看是大于target那么就移动right为mid-1,因为说明此时mid对应数据过大,应该缩小区域,而数组有序递增,右区间肯定都是大数,所以缩小右区间临界点,往左区间移动,而且当前mid又不是答案,所以可以移动成mid-1,同理如果mid对应数据小于target那么就让left=mid+1。

但是本题还有可能要找的数据在数组里不存在,从题目中的测试用例可知,如果要找的数据不存在插入到的位置应该在比当前找的数字大的第一个数的位置停下,表明应该插在这里,那该怎么办呢?

不用担心我们的right仍然可以找到正确答案,因为如果不存在要找的值,那么我们的right会在最后一次的mid大于target判断的时候将right停在mid-1的位置上,也就是这个最后一次的mid应该是我们要插入的数值,最后应该返回right+1,按照这种思路写代码就对了吗?还有两种边界情况我们需要讨论一下,那就是当要插入的数据应该插入在整个数据的尾部或者首部,如果是要插入尾部那返回right+1是对的,因为target过大,会不停右移动left,直到相遇之后退出循环,right都没动,所以返回的right+1位置刚好就是要插入到的位置;如果是target的数据过小,我们会不停移动right向左,直到相遇后的一次移动会使right错开最左边界一位,所以返回right+1正好是0的位置,这也是对的,我们甚至还可以简化一步,把遇到答案值直接返回也写在,判断里而不直接返回,也就是当nums【mid】>=target,right=mid-1;这样如果数组中有target,会在找到的时候right停在target的前一个位置,而且由于right的位置与left的区间中生成的mid过小,left会一直右移动,而且right不会发生改变,因为此时的left——right之间的任意数都小于target。这种简化仅适用于对代码理解很熟悉的写法,初学者尽量不要写如此简化的代码,这样会省去很多细节不利于理解

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

 按照我上面的讲解,我把每句代码的含义都讲的清清楚楚!对照下来是可以看懂所有细节的

简单题细想代码细节还是可以变复杂的,不要小看任何一道简单题!!!

我们给出第二种写法

最近在看acw网站的y老师的二分模板,它的模板都是left<right的,索性我们也看一下left<right的写法,如果读者看过left<right的二分模板会发现它不像之前的left<=right的模板,会使两个边界都错开mid,而是始终保持一个边界等于mid,另一个错开mid,我们简单的来看一下如果left<right时候还像之前那样left和right都错开mid会发生什么?

按照上一题解正常初始化完之后我们进入循环,如果用我们上一个的题解思路去找答案,当此时target数据太小的时候,也就是需要插入的地方是整个数据的最前面的时候,不停移动right向左区间,由于我们判断条件的缘故left<right,right不会移动到left的前面,也就是会在left和right之间还剩两个数字时候是最后一次移动,right移动到0的位置就退出了,而此时我们返回的是right+1答案会是1,这并不行。而是要写成right=mid;而且改动了这里之后读者可以模拟一下不能再返回right+1,一定也要有所改变,写成right的返回。因为要找的答案此时是存在于right正好的位置而不是后一个位置了。

这也是left<right的两种模板,要么right=mid要么left=mid,不能都错开,不然一定会发生错误,至于另一种对应的错误情况以及当不同取值时候mid要怎么取值,这里不再展开细讲,有兴趣的读者可以去看其他人的文章可能有更详细的讲解。

讲了这么多我们没有讲到这道题用left<right时候要注意的另一个重要的点,也就是right的初始化,通常正常初始化left和right为左闭右闭区间然后套模板就能解决,但是本题由于left<right引起早退的情况,不会让target过大的时候,right走到正确的位置,这种情况发生的时候会走到整个数据元素个数-1的位置就退出了,这个时候虽然和我们之前的left一直右移动相同,但是却始终差一个,而我们还不能写成判断如果此时的right==nums.size-1那么直接返回right+1,因为可能要找的答案就是那个位置,所以更好的做法我们把right初始化为nums.size,这样就可以轻松应对这种特例,而如果是能找到或者中间插入和头插的话,都会引起right位置的偏移。

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int l=0,r=nums.size();while(l<r){int mid=l+r>>1;if(nums[mid]>=target){r=mid;}else l=mid+1;}return r;}
};

本题还有一些其他的版本做法,都是在边界下功夫,比如左开右闭、左闭右闭,左闭右开甚至是全开区间的写法,不同的题解可能拥有不同的写法,这里就不再写那么多了,只记住一个或者两个常用的写法就足以应付大多数的题了,记住太多容易搞混。


74.搜索二维矩阵

74. 搜索二维矩阵 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/search-a-2d-matrix/description/?envType=study-plan-v2&envId=top-interview-150二维搜索题,数组由一维变成二维了,该如何做呢?

这道题大体有两种做法,第一种是将二维数组展开为一维,然后因为数组严格递增,所以采用二分去找答案,非常容易,另一种是由于二维数组严格递增性,可以找各行的开头数字和目标值进行比较,直到比较到某时候小于了某一行的开头数字,增明我们要找的数字可能存在于该行的前一行。

当然了,并不一定能找得到,因为我们是判断数组里是否存在这个数字,是可以找不到的。

这里我们只给出第一种解法,因为个人喜欢第一种清晰的思路,然后我们使用两种不同的判断条件二分方法来比较它们。

第一种是left小于等于right,我们把二维数组拆开成一维数组的思路就是用两个变量表示二维数组的行和列,然后r初始化为行列乘积,当遍历数组时候用mid/列数和mid%列数代表数组的行和列坐标,为什么要除以列数呢?这是因为我们把二维展为一维相当于把第二行拼在第一行后面,第三行拼在第二行后面………所以除以列数知道它是哪一行拼上来的,模上列数就是第几个数字,因为不管在第几行,一行所拥有的列数是不变的!

知道了如何展开数组之后,就简单了,和普通的二维找数没有什么区别,在循环里判断如果当前数字是正确的target,那么直接return,如果小于target,l=mid+1,如果mid大于targetr=mid-1,这么写是为了简单直观,如果读者不习惯这样写也可以写成让一个大于等于,另一个正常小于,然后出循环后取那个下标的+1或减1即可,具体取决于实现。

下面给出代码

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m=matrix.size(),n=matrix[0].size();int l=0,r=n*m-1;while(l<=r){int mid=(l+r)>>1;if(matrix[mid/n][mid%n]==target)return true;if(matrix[mid/n][mid%n]>target)r=mid-1;else l=mid+1;}return false;}
};

再然后是left<right的实现方法,这种实现方法需要特判,由于left小于right的缘故,数组只有一个数字时候进不去循环(写成n*m-1时候),我们要在return处写上一个特判判断单行存在时候,当前下标是否等于target

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m=matrix.size(),n=matrix[0].size();int l=0,r=n*m-1;while(l<r){int mid=(l+r)>>1;if(matrix[mid/m][mid%n]==target)return true;else if(matrix[mid/m][mid%n]>target)r=mid;else l=mid+1;}return matrix[l/m][l%n]==target;}
};

本期内容就到这里
如果对您有用的话别忘了一键三连哦,如果是互粉回访我也会做的!

大家有什么想看的题解,或者想看的算法专栏、数据结构专栏,可以去看看往期的文章,有想看的新题目或者专栏也可以评论区写出来,讨论一番,本账号将持续更新。
期待您的关注

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git 使用教程(超级详细)

目录 一&#xff1a;Git二&#xff1a;SVN与Git的的区别三、安装Git四&#xff1a;常规操作五&#xff1a;远程仓库六&#xff1a;创建与合并分支七&#xff1a;bug分支八&#xff1a;多人协作九&#xff1a;git可视化工具 Git Git 是一种分布式版本控制系统&#xff0c;用于…

现代雷达车载应用——第2章 汽车雷达系统原理 2.6节 雷达设计考虑

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.6 雷达设计考虑 上述部分给出了汽车雷达基本原理的简要概述。在雷达系统的设计中&#xff0c;有几个方面是必不可少的&#xff0c;它们决定了雷达系…

【leetcode】链表总结

说明&#xff1a;本文内容来自于代码随想录 链表基本操作 https://leetcode.cn/problems/design-linked-list/ 删除节点 https://leetcode.cn/problems/remove-linked-list-elements/description/&#xff0c;删除节点&#xff0c;虚拟头节点。定义两个节点&#xff0c;分别…

基于QTreeWidget实现带Checkbox的多级组织结构选择树

基于QTreeWidget实现带Checkbox的多级组织结构选择树 采用基于QWidgetMingw实现的原生的组织结构树 通过QTreeWidget控件实现的带Checkbox多级组织结构树。 Qt相关系列文章&#xff1a; 一、Qt实现的聊天画面消息气泡 二、基于QTreeWidget实现多级组织结构 三、基于QTreeWidget…

eclipse连接mysql数据库(下载eclipse,下载安装mysql,下载mysql驱动)

前言&#xff1a; 使用版本&#xff1a;eclipse2017&#xff0c;mysql5.7.0&#xff0c;MySQL的jar建议使用最新的&#xff0c;可以避免警告&#xff01; 1&#xff1a;下载安装&#xff1a;eclipse&#xff0c;mysql在我之前博客中有 http://t.csdnimg.cn/UW5fshttp://t.csdn…

2023年最详细的:本地Linux服务器安装宝塔面板,并内网穿透实现公网远程登录

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一…

插头是什么

插头 电工电气百科 文章目录 插头前言一、插头是什么二、插头的类别三、插头的作用原理总结前言 插头的设计和结构会根据不同的国家和地区的标准和电源类型而有所不同。所以,在使用插头时,需要注意使用符合当地标准和规定的插头,以确保电气安全以及插入正确的电源插座 一、…

【lombok】从easyExcel read不到值到cglib @Accessors(chain = true)隐藏的大坑

背景: 在一次使用easyExcel.read 读取excel时&#xff0c;发现实体类字段没有值&#xff0c;在反复测试后&#xff0c;发现去掉Accessors(chain true)就正常了&#xff0c;为了验证原因&#xff0c;进行了一次代码跟踪 由于调用链路特别长&#xff0c;只列举出部分代码&#x…

动态规划习题

动态规划的核心思想是利用子问题的解来构建整个问题的解。为此&#xff0c;我们通常使用一个表格或数组来存储子问题的解&#xff0c;以便在需要时进行查找和使用。 1.最大字段和 #include <iostream> using namespace std; #define M 200000int main() {int n, a[M], d…

LCR 120. 寻找文件副本

解题思路&#xff1a; 利用增强for循环遍历documents&#xff0c;将遇见的id加入hmap中&#xff0c;如果id在hamp中存在&#xff0c;则直接返回id class Solution {public int findRepeatDocument(int[] documents) {Set<Integer> hmapnew HashSet<>();for(int d…

Python+Selenium UI自动化测试环境搭建及使用

一、什么是Selenium &#xff1f; Selenium 是一个浏览器自动化测试框架&#xff0c;它主要用于web应用程序的自动化测试&#xff0c;其主要特点如下&#xff1a;开源、免费&#xff1b;多平台、浏览器、多语言支持&#xff1b;对web页面有良好的支持&#xff1b;API简单灵活易…

【C++11特性篇】盘点C++11中三种简化声明的方式【auto】【decltype】【nullptr】(3)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.auto&#xff06;范围for二.decltyp…

Java医院3D人体智能导诊系统源码 Uniapp+springboot 微信小程序

“智能导诊”以人工智能手段为依托&#xff0c;为人们提供智能分诊、问病信息等服务&#xff0c;在一定程度上满足了人们自我健康管理、精准挂号等需求。智能导诊可根据描述的部位和病症&#xff0c;给出适合病症的科室参考。 智能导诊页面会显示男性或女性的身体结构图&#x…

Linux基本操作指令

哈喽小伙伴们&#xff0c;从这篇文章开始&#xff0c;在学习数据结构的同时&#xff0c;我们开启一个新的篇章——Linux操作系统的学习&#xff0c;这将会是又一个新的开始&#xff0c;希望小伙伴们能够认真细心&#xff0c;不要掉队哦。 目录 一.什么是Linux 二.为什么要学习…

LeetCode 300最长递增子序列 674最长连续递增序列 718最长重复子数组 | 代码随想录25期训练营day52

动态规划算法10 LeetCode 300 最长递增子序列 2023.12.15 题目链接代码随想录讲解[链接] int lengthOfLIS(vector<int>& nums) {//创建变量result存储最终答案,设默认值为1int result 1;//1确定dp数组&#xff0c;dp[i]表示以nums[i]为结尾的子数组的最长长度ve…

JNA实现JAVA调用C/C++动态库

1.JNA JNA全称Java Native Access&#xff0c;是一个建立在经典的JNI技术之上的Java开源框架&#xff08;https://github.com/twall/jna&#xff09;。JNA提供一组Java工具类用于在运行期动态访问系统本地库&#xff08;native library&#xff1a;如Window的dll&#xff09;而…

CSS学习笔记整理

CSS 即 层叠样式表/CSS样式表/级联样式表&#xff0c;也是标记语言&#xff0c; 用于设置HTML页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式、边距&#xff09;以及版面的布局和外观显示样式 目录 准备工作 Chrome调…

时间序列预测 — CNN-LSTM实现多变量多步光伏预测(Tensorflow)

目录 1 数据处理 1.1 导入库文件 1.2 导入数据集 1.3 缺失值分析 2 构造训练数据 ​3 模型训练 3.1 CNN-LSTM网络 3.2 模型训练 4 模型预测 专栏链接&#xff1a;https://blog.csdn.net/qq_41921826/category_12495091.html 1 数据处理 1.1 导入库文件 import scip…

『番外篇二』Swift “黑魔法”之动态获取类实例隐藏属性的值

概览 在 Swift 代码的调试中,我们时常惊叹调试器的无所不能:对于大部分“黑盒”类实例的内容,调试器也都能探查的一清二楚。 想要自己在运行时也能轻松找到 Thread 实例“私有”属性的值吗(比如 seqNum)? 在本篇博文中您将学到如下内容: 概览1. 借我,借我,一双慧眼吧…

深入理解LightGBM

1. LightGBM简介 GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型&#xff0c;其主要思想是利用弱分类器&#xff08;决策树&#xff09;迭代训练以得到最优模型&#xff0c;该模型具有训练效果好、不易过拟合等优点。GBDT不仅在工业界应用广泛&#…