【LeetCode刷题笔记】双指针

news2024/11/18 7:34:03

剑指 Offer 21.调整数组顺序使奇数位于偶数前面

解题思路:
  • 对撞指针 从左边不停的找第一个偶数,从右边不停的找第一个奇数 ,找到后 交换 两者位置

本题与【905. 按奇偶排序数组】几乎雷同。

剑指 Offer 57.和为s的两个数字

本题与【167. 两数之和 II - 输入有序数组】相同

解题思路:
  • 1) 对撞指针 ,计算  sum(L + R)  的和,判断与  target  的关系,来决定移动左指针还是右指针
  • 2) 二分查找 ,先固定一个  nums[i] ,然后到剩余数组 [ i+1...end ] 中二分查找  target = s - nums[i]  的值
  • 注意:题目数组是 递增排序 好的,所以才能用上面两种方法,并且只要找到一个解即可返回。

 

26.删除有序数组中的重复项

解题思路:
  • 1) 快慢指针 slow 指向 已处理区域 最后一个位置 fast 指向 未处理区域 第一个位置 初始 slow = 0, fast = 1,不断向后移动 fast
  • fast 的值跟 slow 的值 不等 ,就让 slow++ ,把 fast 处的值放到 slow 位置,最后返回 slow + 1  作为 数组长度 。 
  • 注意:本题能这样做的原因是因为题目的数组是 升序排列 的。

 

这种算法是不断比较未处理区域的值和已处理区域的最后一个值,由于数组是升序排列的,所以只需要跳过那些与已处理区域最后一个相同的值即可。然后只将不同的值往前面放。 

 

解题思路: 
  • 2) 快慢指针 slow 指向 已处理区域 下一个位置 fast 指向 未处理区域 第一个位置 初始 slow = 1, fast = 1 不断向后移动 fast
  • fast 的值跟 fast-1 不同时,就把 fast 处的值放到 slow 位置,然后 slow++ 最后返回 slow 作为 数组长度 。 
  • 注意:本题能这样做的原因是因为题目的数组是 升序排列 的。

 

这种算法是不断比较未处理区域的当前值和上一个值,由于数组是升序排列的,所以只需要跳过那些与上一个相同的值即可。然后只将不同的值往前面放。 

问题:为什么 slow fast 要从 1 位置开始呢?

这是因为 slow 的含义是已处理区域的下一个位置,初始时,前面预留的 0 位置表示该位置默认是已经被处理的区域,即 1 个数字本身是不重复的,所以 slow 从 0 位置的下一个位置开始。而 1 位置表示未处理的第一个位置,所以 fast 1 位置开始。在方法 1 中 slow 从 开始的含义也是一样的,表示此时 0 位置是已处理的最后一个位置(同时也是已处理的第一个位置)。

80. 删除有序数组中的重复项 II

 

解题思路:
  • 类似26, slow 指向 已处理区间 下一个位置,   slow 前面 预留两个空位 ,  slow、fast 从第 3 个元素开始,即初始 slow = 2, fast = 2 ,   不断向后移动 fast
  • fast slow - 2 位置的元素不同,就把 fast 处的值放到 slow 的位置,然后 slow++ 最后返回 slow 作为数组长度。
  • 注意:本题能这样做的原因是因为题目的数组是 有序排列 的。

 

 

这里之所以前面预留2个空位,是因为题目要求重复元素只出现2次,因此如果前两个元素重复也没有关系,如果前两个元素不同,更没有问题。

还有一个问题是为什么跟 fast 比较的是 slow - 2 位置的数呢?

如果 fast slow - 2 相同,而 slow - 2 又可能与 slow - 1 相同,那么就有可能出现三个相同的数: nums[fast] == nums[slow - 2] == nums[slow - 1],此时重复元素出现次数 > 2 了,因此 fast 必须跟 slow - 2 保持不同,这样重复元素最多出现 2 次。

那如果 fast slow - 1 比较不同时就赋值,这样行不行呢?如下图所示,这样会完美的错过后面所有重复数字的答案(重复的2和3只保留了一个)

 

287. 寻找重复数

解题思路:
  • 1)快慢指针,存在重复数字的数组会形成环,类似141环形链表, 环的入口处即重复元素
  • 将题目给的数组当作 一个数组形式的链表 来看待,数组的下标就是指向元素的指针,把数组的元素也看作指针。
  • 例如 0 是指针,指向 nums[0] ,而 nums[0] 也是指针,指向 nums[nums[0]]
  • slow fast 初始为 0 ,然后 slow 走一步, fast 走两步,如果 slow 追上 fast 就退出循环, 此时让 slow = 0 回到开头,然后 slow fast 同步走,如果 slow 追上 fast , 则相遇点就是重复数字。 

看一下为什么题目给定的存在重复数字的数组会形成环: 

如上图所示:我们使用函数 f(x) = nums[x] 来建立一个数列:x,nums[x],nums[nums[x]], nums[nums[nums[x]]]...... 即每一个数的位置是前一个数的值,如果我们从 x=nums[0] 开始,又因为 nums 里有一个重复的数字,这必将形成一个带有循环的数列。

更复杂的例子/更大的圈:

我们不难看出,进入循环的点就是我们要找的重复的数字(即上面两个例子里的 1 和 9),问题是如何找到它?答案就是使用快慢指针

解题思路: 
  • 2) 二分查找 在  [1, n] 数字区间上二分, 对于 mid = (L + R) / 2 ,重复的数要么落在 [L, mid]  要么落在 [mid + 1, R]
  • 每次二分后统计数组中 ≤ mid  的元素的个数 count ,然后根据 count 和  mid  的大小关系来决定接下来到哪边去二分:
  • ① 如果 count ≤ mid ,说明左边没有重复,收缩左边界 L = mid + 1 ,到右半边区间 [mid + 1, R] 查找;
  • ② 如果 count  >  mid ,说明 [L, mid] 内出现了重复元素, 收缩 R mid - 1 ,到左半边区间 [L, mid - 1] 继续查找,但是此时需要用变量 res 记录一下移出的 mid 值,因为它可能是潜在的重复元素。最后返回 res 即可。

为什么二分时要统计 ≤ mid 的元素个数呢?

简单的来说就是一个萝卜一个坑,mid 及左侧最多有 mid 个坑(如果包含1~n连续不重复的话),如果出现了重复元素,就可能超过 mid 个坑。 

我们考虑一个 cnt 数组,cnt[i] 表示 nums 中小于等于 i 的元素个数,如下图:

假设nums数组无重复的情况下,显然 cnt[i] ≤ nums[i],也就是说 ≤ mid 的数量不会超过mid,例如上图中 ≤ 3 的数量最多就是 3 个,但是如果数组中有 1 个重复元素的情况下,就不一定能满足这个条件了:

显然这是因为出现了重复数字导致的。 

虽然题目数组是无序的,但是cnt数组是逐步增大且具有单调性的,此题的二分思想就是利用cnt数组的特性来判断区间划分的。

解题思路: 
  • 3) 位运算 统计 nums 中所有人的二进制在第 i 位上 1 的个数和记为 x , 统计 [1-n] 中所有人的二进制在 i 位上 1 个数和记为  y ,如果 x > y , 则说明重复的数在第 i 位上二进制是 1 ,因此才会多出来  个数。我们只需将 32 位二进制位中 1 的个数多出的那一位的答案二进制位设置为  即可。

 

这个代码如果想更极致一点,可以先找出最大数 n 中最高位的 1 的位置,循环只需要处理到这一位即可,因为再高的就全是 0 了。参考代码:

27. 移除元素

 

解题思路:
  • 1) 快慢指针 ,快慢指针从 0 开始,不断移动 快指针 ,把 快指针 遇到的 不等于 val 的值移动到 慢指针 的位置。

这个做法可以看到是利用原始数组本身来作为结果数组接收答案。 

解题思路: 

  • 2) 对撞指针 ,当  L 指针 遇到要删除的元素  val  时,使用数组最后一个元素来覆盖这个元素,并且让 R--
  • 覆盖后的  处新数字有可能仍是要删除的元素,没有关系,在下一次 while 循环会接着判断处理,但是至少,我们从数组的最后删除了一个元素。
  •   L 指针 遇到的元素不是  val  时,让 L++
  • while  循环条件是 L<=R ,因为每个数都要处理,最后返回 作为数组长度。

283. 移动零

解题思路:
  • 1) 快慢指针 ,快慢指针从 0 开始,不断移动快指针,当 快指针 遇到的 非 0 数字 时, 交换快慢指针的元素即可。
  • 2)   快慢指针 ,快慢指针从 0 开始,不断移动快指针, 快指针 遇到的 非 0 数字 时, 直接覆盖到前面 slow 位置,然后 slow++ 。全部结束后,再把 slow 之后的位置都置为  0
  • 3) 快慢指针 ,针对方法 2)优化: fast 遇到 非0 值覆盖到 slow 后,如果 fast 不等于 slow 就将 fast 直接原地置 0 ,然后再 slow++

 

 

202. 快乐数

解题思路:
  • 1) 哈希检测环 ,使用 HashSet 来判断重复,每次 while 循环判断只要  n != 1  并且也没包含在 set 中,就将  n  并加入 set 集合中,然后将 n 更新为 n 的各位数的平方和。
  • 结束循环后返回 n 是否等于 1 即可(跳出循环只有两种情况要么 n 变成 1 要么 set 集合中出现了重复即有环)。
  • 求  num  各位的平方和的技巧:每次  num % 10  取出个位数,然后计算平方累加到结果中, num / 10  去掉个位数,直到  num  变成  停止。 

虽然这道题在 LeetCode 上标记为 [简单] 难度,但是我觉得有一种情况是比较难观察出来的,那就是题目中说的除了最终得到 1 以外,最终也可以变成无限循环,这里的无限循环有可能是最终又遇到了重复的数字,也有可能是变得越来越大直到无穷大一直进行下去。也就是说可能会有两种情况,而在很多网课中讲解的这里仅仅只是默认按照重复数字套哈希表的解法。

下面看一下力扣官方的解释:

我们可以先举几个例子。我们从7开始。则下一个数字是49(因为72=49),然后下一个数字是97(因为42+92=97)。我们可以不断重复该的过程,直到我们得到1。因为我们得到了1,我们知道7是一个快乐数,函数应该返回 true

 

再举一个例子,让我们从 116  开始。通过反复通过平方和计算下一个数字,我们最终得到 58 ,再继续计算之后,我们又回到 58。由于我们回到了一个已经计算过的数字,可以知道有一个循环,因此不可能达到 1。所以对于 116,函数应该返回 false。 

 

根据我们的探索,我们猜测会有以下三种可能:

    1.最终会得到 1。

    2.最终会进入循环。

    3.值会越来越大,最后接近无穷大。

第三个情况比较难以检测和处理。我们怎么知道它会继续变大,而不是最终得到 1 呢?我们可以仔细想一想,每一位数的最大数字的下一位数是多少。

 

对于 3 位数的数字,它不可能大于 243。这意味着它要么被困在 243 以下的循环内,要么跌到 1。4 位或 4 位以上的数字在每一步都会丢失一位,直到降到 3 位为止。所以我们知道,最坏的情况下,算法可能会在 243 以下的所有数字上循环,然后回到它已经到过的一个循环或者回到 1。但它不会无限期地进行下去,所以我们排除第三种选择。

即使在代码中你不需要处理第三种情况,你仍然需要理解为什么它永远不会发生,这样你就可以证明为什么你不需要处理它。

解题思路: 
  • 2) 快慢指针检测环 ,慢指针和快指针初始值从 n 开始,在每次循环中,慢指针计算更新 1 次( slow 更新为其各位平方和),而快指针计算更新 2 fast 更新为其各位平方和) ,直到出现 快指针 变为 1 ,或者 快指针 慢指针 相遇,就退出循环。
  • 最后返回 快指针是否等于1 即可(如果是因为快慢指针相等退出循环的,说明存在环)。
  • 注意: 快指针 会先遇到 1

注意上面代码中,最好使用 do-while 循环,不然可能上来就退出循环了,因为 slow fast 都初始化为 n  ,当然由于前面已经分析了要么最终会变成1,要么会出现环(重复),所以你也可以直接写一个 while(true) 死循环来检测,在循环体里面写判断退出的条件。

快慢指针是用来检测有环问题的经典做法。

881. 救生艇

解题思路:
  • 1) 排序 + 对撞指针 ,要求船数最少,那就应该尽可能多的坐满  人一船,剩下的人一人坐一船,因此 先排序 ,然后对撞指针从两头往中间挑选两个人的体重之和 <=limit 的坐一船,如果两个人的体重超过 limit ,只能较重的那个人自己坐一船
  • 循环结束时,如果  L == R ,则剩余的 1 人自己坐一船。 (注意退出循环也有可能是因为 L > R,所以最后返回时需要判断一下)
解题思路: 
  • 2) 排序 + 中心扩展, 也是需要 先排序 ,然后从 右往左 找到第一个 <= limit / 2 的位置,然后从该位置开始设双指针往 左右 同时 扩展 寻找能够两个人坐一船的,剩下落单的人只能一人一船。
  • 特判1:如果每一个人的体重都超过了限重的一半( > limit / 2 ),那么只能一人坐一船,直接返回 people.length ,不用再继续了。因为此时任意两人体重之和都会超过 limit
  • 特判2:如果最重的人体重 <= limit / 2 ,则此时任意两人组合体重和都不会超过 limit ,所以此时可直接安排 2 人坐一船,直接返回 (people.length + 1) / 2

 

中心扩展法计算稍显麻烦,相对来说,本题还是方法 1)对撞指针更加容易理解,代码也更加简单。

 

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

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

相关文章

SpringCloud-消息组件

1 简介 了解过RabbitMQ后&#xff0c;可能我们会遇到不同的系统在用不同的队列。比如系统A用的Kafka&#xff0c;系统B用的RabbitMQ&#xff0c;但是没了解过Kafka&#xff0c;因此可以使用Spring Stream&#xff0c;它能够屏蔽地产&#xff0c;像JDBC一样&#xff0c;只关心SQ…

超聚变安装银河麒麟服务器系统ky10-server-x86

超聚变-通过BMC口进服务器管理界面 如下图为BMC管理口&#xff1a; 默认BMC口IP&#xff1a;https://192.168.2.100 l浏览器打开网页&#xff0c;输入用户名和密码 登陆后&#xff0c;如下图红框处&#xff0c;选择“启动虚拟控制台”——选择“HTML集成远程控制台” 系统银河麒…

绘画的颜料

Alcohol Ink 酒精油墨 酒精墨水风格是一种绘画技术&#xff0c;使用酒精为基础的墨水创造出充满活力和丰富多彩的设计。 墨水是半透明的&#xff0c;可以分层以达到深度和复杂性。 Alcohol ink painting of a husky, side viewAcrylic 丙烯颜料 色彩鲜艳的多功能涂料。它可以…

基于Java的蛋糕甜品系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&…

Seata 源码篇之AT模式启动流程 - 下 - 04

Seata 源码篇之AT模式启动流程 - 下 - 04 全局事务提交分支事务全局提交全局事务回滚分支事务全局回滚小结 本系列文章: Seata 源码篇之核心思想 - 01Seata 源码篇之AT模式启动流程 - 上 - 02Seata 源码篇之AT模式启动流程 - 中 - 03 上一篇文章&#xff0c;我们看了Seata AT…

全志ARM926 Melis2.0系统的开发指引⑧

全志ARM926 Melis2.0系统的开发指引⑧ 编写目的12.5. 应用程序编写12.5.1. 简单应用编写12.5.1.1. 注册应用12.5.1.2. 创建管理窗口12.5.1.3. 实现管理窗口消息处理回调函数12.5.1.4. 创建图层12.5.1.5. 创建 framewin12.5.1.6. 实现 framewin 消息处理回调函数 -. 全志相关工具…

JavaScript系列从入门到精通系列第十五篇:JavaScript中函数的实参介绍返回值介绍以及函数的立即执行

文章目录 一&#xff1a;函数的参数 1&#xff1a;形参如何定义 2&#xff1a;形参的使用规则 二&#xff1a;函数的返回值 1&#xff1a;函数返回值如何定义 2&#xff1a;函数返回值种类 三&#xff1a;实参的任意性 1&#xff1a;方法可以作为实参 2&#xff1a;将匿…

Next.js 入门笔记

前言 之前初步体验了 React 的魅力, 又看文档理解了一下 useState 和 useEffect, 目前初步理解的概念是: useState 用来声明在组件中使用并且需要修改的变量 useEffect 用来对 useState 声明的变量进行初始化赋值 可能理解的不太准确, 不过大概差不多是这么个意思. 但是再往后…

Qt之显示PDF文件

之前使用过mupdf库&#xff0c;能够成功显示pdf&#xff0c;但是我用着有BUG&#xff0c;不太理解它的代码&#xff0c;搞了好久都不行。后面又试了其他库&#xff0c;如pdfium、popler、下载了很多例程&#xff0c;都跑不起来&#xff01;后面偶然得知xpdf库&#xff0c;看起来…

【C++设计模式之建造者模式:创建型】分析及示例

简介 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它将复杂对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 描述 建造者模式通过将一个复杂对象的构建过程拆分成多个简单的部分&#xff0c;并由不同…

华为云云耀云服务器L实例评测|部署个人音乐流媒体服务器 navidrome

华为云云耀云服务器L实例评测&#xff5c;部署个人音乐流媒体服务器 navidrome 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 产品规格1.3 产品优势1.4 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 navidrome3.1 navidrome 介绍3.…

全志ARM926 Melis2.0系统的开发指引⑦

全志ARM926 Melis2.0系统的开发指引⑦ 编写目的11. 调屏11.1. 调屏步骤简介11.1.1. 判断屏接口。11.1.2. 确定硬件连接。11.1.3. 配置显示部分 sys_config.fex11.1.3.1. 配置屏相关 IO 11.1.4. Lcd_panel_cfg.c 初始化文件中配置屏参数11.1.4.1. LCD_cfg_panel_info11.1.4.2. L…

网课搜题 小猿题库多接口微信小程序源码 自带流量主

多接口小猿题库等综合网课搜题微信小程序源码带流量主&#xff0c;网课搜题小程序, 可以开通流量主赚钱 搭建教程1, 微信公众平台注册自己的小程序2, 下载微信开发者工具和小程序的源码3, 上传代码到自己的小程序 源码下载&#xff1a;https://download.csdn.net/download/m0_…

【C语言经典100例题-70】求一个字符串的长度(指针)

代码 使用指针来遍历字符串&#xff0c;直到遇到字符串结尾的空字符\0为止&#xff0c;统计字符数量即为字符串长度。 #include<stdio.h> #define n 20 int getlength(char *a) {int len 0;while(*a!\0){len;a;}return len; } int main() {char *arr[n] { 0 };int l…

软技能继续挑战网络安全领域

根据 ISACA 的一份新报告&#xff0c;新的网络安全调查结果指出了网络安全专家缺乏的领域&#xff0c;其中人际技能、云计算和安全措施是网络安全专家最突出的技能缺陷。 59% 的网络安全领导者表示他们的团队人手不足。50% 的受访者表示有非入门级职位的职位空缺&#xff0c;而…

多媒体应用设计师

1.多媒体技术基础 1.1.媒体与技术 1.1.媒体 维基百科&#xff1a;传播信息载体 国际电信联盟&#xff08;ITU-T&#xff09;&#xff1a;感知、表示、存储和传输的手段和方法。 两层含义&#xff1a;存储信息的实体&#xff0c;媒质。传递信息载体&#xff0c;媒介。 1.2.国…

使用Python优雅的绘制甘特图

简介 Gantt图表是一种条形图&#xff0c;用于描绘项目进度。图表在垂直轴上列出要执行的任务&#xff0c;在水平轴上列出时间间隔。图中水平条的宽度显示每个活动的持续时间。在Python中&#xff0c;我们可以使用Plotly库来创建和展示Gantt图表。 基础的Gantt图表 首先&…

beego-简单项目写法--路径已经放进去了

Beego案例-新闻发布系统 1.注册 后台代码和昨天案例代码一致。,所以这里面只写一个注册的业务流程图。 **业务流程图 ** 2.登陆 业务流程图 登陆和注册业务和我们昨天登陆和注册基本一样&#xff0c;所以就不再重复写这个代码 但是我们遇到的问题是如何做代码的迁移&…

计算机专业毕业设计项目推荐12-志愿者管理系统(Spring+Js+Mysql)

志愿者管理系统&#xff08;SpringJsMysql&#xff09; **介绍****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程以及模式&#xff0c;在编写的过程…

[NISACTF 2022]join-us - 报错注入无列名注入

点击登录&#xff0c;找到注入点 这种框&#xff0c;可以直接爆破关键字&#xff0c;看是否拦截&#xff0c;也可以手动尝试&#xff0c;发现、union、and、or、substr、database等关键字都拦截了 1、学到了&#xff1a;可以用数据库中不存在的表名或者不存在的自定义函数名爆…