关于每个知识点的例题
- 可以自己看力扣标准题解。也可以在哔哩哔哩上看。
- 想看我的,就到github 看 - 库 ,介绍里写的算法讲解那些,里面有知识点,有题库。题库,每天都发题,可能跟博客的进度不一样。因为我上传和整理以及做题需要时间,大家可以私发信息给我,我会尽量提前整理你们要的东西。
- 这个是github链接,找 - 库,看里面的题库对应知识点部分,就能找到题
- 题库里肯定不会只有这几道双指针题,只要我做到一道,就会交一道。这些只是为了理解知识点而用的。
- 觉得不错的话,麻烦点个赞给github。谢谢。
一、了解双指针
双指针(Two Pointers)是一种常用的算法技巧,通常用于解决数组或链表中的问题。它的核心思想是使用两个指针(或索引)在数据结构中遍历或搜索,从而高效地解决问题。双指针技巧通常可以将时间复杂度从 (O(n^2)) 优化到 (O(n))。
推荐先看这个视频,再看下面文章
二、 双指针的常见应用场景
-
有序数组的两数之和:
- 给定一个有序数组和一个目标值,找到两个数使它们的和等于目标值。
- 使用双指针,一个指向数组开头,一个指向数组末尾,根据和的大小移动指针。
-
移除数组中的重复元素:
- 给定一个有序数组,原地移除重复元素。
- 使用双指针,一个指针用于遍历数组,另一个指针用于记录非重复元素的位置。
-
滑动窗口问题:
- 给定一个数组和一个窗口大小,找到满足条件的子数组。
- 使用双指针表示窗口的左右边界,根据条件滑动窗口。
-
链表的快慢指针:
- 判断链表是否有环,或找到链表的中间节点。
- 使用快慢指针,快指针每次移动两步,慢指针每次移动一步。
二、 双指针的技巧总结
1. 对撞指针:
对撞指针 是一种常用的双指针技巧,通常用于解决数组或字符串中的问题。它的核心思想是使用两个指针,一个从数组的起始位置(左指针)开始,另一个从数组的末尾位置(右指针)开始,通过向中间移动来解决问题。
1.1、对撞指针的核心思想
-
指针的初始化:
- 左指针
l
指向数组的起始位置(l = 0
)。 - 右指针
r
指向数组的末尾位置(r = nums.size() - 1
)。
- 左指针
-
指针的移动规则:
- 根据问题的要求,决定左指针或右指针的移动方向。
- 通常,左指针向右移动,右指针向左移动。
-
终止条件:
- 当左指针和右指针相遇或交叉时,算法结束。
1.2、对撞指针的总结
-
适用场景:
- 有序数组中的两数之和、三数之和。
- 反转数组或字符串。
- 验证回文串。
- 盛最多水的容器。
-
核心思想:
- 使用两个指针从数组的两端向中间移动,通过比较指针指向的元素来解决问题。
-
时间复杂度:
- 通常为 (O(n)),因为每个元素最多被访问一次。
-
空间复杂度:
- 通常为 (O(1)),只使用了常数级别的额外空间。
2. 快慢指针:
快慢指针 是一种常用的双指针技巧,通常用于解决链表或数组中的问题。它的核心思想是使用两个指针,一个移动速度快(快指针),一个移动速度慢(慢指针),通过它们的相对移动来解决问题。
2.1、快慢指针的核心思想
-
快指针和慢指针的移动速度:
- 快指针每次移动两步。
- 慢指针每次移动一步。
-
相对速度:
- 快指针和慢指针的相对速度是 1,即快指针每次比慢指针多走一步。
-
终止条件:
- 当快指针到达链表末尾(或数组末尾)时,慢指针通常指向目标位置。
2.2、快慢指针的总结
-
适用场景:
- 链表中的环检测、中间节点查找。
- 数组中的重复元素删除、重复数查找。
-
核心思想:
- 快指针和慢指针以不同的速度移动,通过相对速度解决问题。
-
时间复杂度:
- 通常为 (O(n)),因为每个元素最多被访问两次。
-
空间复杂度:
- 通常为 (O(1)),只使用了常数级别的额外空间。
3. 滑动窗口:
3.1、滑动窗口的两种情况
3.1.1、情况1:固定大小的窗口
- 特点:
- 窗口的大小是固定的。
- 通常用于解决需要在固定长度的子数组或子串中查找满足条件的问题。
- 示例问题:
- 在一个数组中,找到长度为
k
的连续子数组,使得子数组的和大于target
。
- 在一个数组中,找到长度为
- 解决方法:
- 初始化左指针
l = 0
,右指针r = k - 1
。 - 计算窗口内的和,如果满足条件,返回结果。
- 如果不满足条件,移动窗口:
l++
,r++
,继续检查新的窗口。
- 初始化左指针
3.1.2、情况2:可变大小的窗口
- 特点:
- 窗口的大小不固定。
- 通常用于解决需要在不定长度的子数组或子串中查找满足条件的问题。
- 示例问题:
- 在一个数组中,找到和大于等于
target
的最短连续子数组。
- 在一个数组中,找到和大于等于
- 解决方法:
- 初始化左指针
l = 0
,右指针r = 0
。 - 右指针
r
向右移动,扩大窗口,直到窗口内的和满足条件。 - 左指针
l
向右移动,缩小窗口,尝试找到更短的满足条件的子数组。
- 初始化左指针
3.4、滑动窗口的核心思想
-
窗口的定义:
- 窗口是数组或字符串中的一个连续子区间,由左指针
l
和右指针r
定义。
- 窗口是数组或字符串中的一个连续子区间,由左指针
-
窗口的移动:
- 扩大窗口:右指针
r
向右移动,增加窗口的大小。 - 缩小窗口:左指针
l
向右移动,减少窗口的大小。
- 扩大窗口:右指针
-
条件的判断:
- 在窗口移动的过程中,根据问题的要求判断当前窗口是否满足条件。
4. 分离指针:
分离指针 是一种双指针技巧,通常用于解决涉及多个数组或链表的问题。它的核心思想是使用两个指针分别遍历不同的数组或链表,通过它们的相对移动来解决问题。
4.1、分离指针的常见应用场景
-
合并两个有序数组或链表:
- 将两个有序数组合并为一个有序数组。
- 将两个有序链表合并为一个有序链表。
-
查找两个数组的交集:
- 找到两个有序数组的交集。
-
判断一个数组是否是另一个数组的子序列:
- 判断一个数组是否是另一个数组的子序列。
-
滑动窗口问题:
- 使用两个指针分别表示窗口的左右边界。
4.2、分离指针的核心思想
-
指针的初始化:
- 每个指针分别指向一个数组或链表的起始位置。
-
指针的移动规则:
- 根据问题的要求,决定每个指针的移动方向。
- 通常,指针的移动方向是单向的(从左到右)。
-
终止条件:
- 当某个指针到达数组或链表的末尾时,算法结束。
4.3、分离指针的总结
-
适用场景:
- 合并两个有序数组或链表。
- 查找两个数组的交集。
- 判断一个数组是否是另一个数组的子序列。
-
核心思想:
- 使用两个指针分别遍历不同的数组或链表,通过比较指针指向的元素来解决问题。
-
时间复杂度:
- 通常为 (O(m + n)),其中 (m) 和 (n) 是两个数组或链表的长度。
-
空间复杂度:
- 通常为 (O(1)),只使用了常数级别的额外空间。
三、 双指针的时间复杂度
- 双指针通常可以将时间复杂度从 (O(n^2)) 优化到 (O(n)),因为每个指针最多遍历数组或链表一次。
四、例题
- 对撞指针
力扣hot100两数之和 - 快慢指针
力扣hot100移动零 - 滑动窗口
力扣LCR长度最小的子数组 - 分离指针
力扣21、合并2个有序链表