labuladong刷题day3-数组使用双指针技巧
Q1题目链接:https://leetcode.com/problems/remove-duplicates-from-sorted-array/
leetcode 26
看到题目的第一思路:
快慢指针技巧:我们让慢指针 slow
走在后面,快指针 fast
走在前面探路,找到一个不重复的元素就赋值给 slow
并让 slow
前进一步。
这样,就保证了 nums[0..slow]
都是无重复的元素,当 fast
指针遍历完整个数组 nums
后,nums[0..slow]
就是整个数组去重之后的结果。
代码随想录之后的想法和总结:
Q: 为什么返回的是 slow + 1
:
索引是从 0 开始的,所以如果 slow
的值是 2,说明数组中有 3 个唯一元素(索引 0、1、2 对应 3 个元素)。因此,返回的数量是 slow + 1
。
Q: 快指针如何赋值给慢指针?
当 nums[fast]
和 nums[slow]
不相等时,说明找到了一个新的唯一元素,于是:
-
slow
增加 1,表示我们准备把新找到的唯一元素放到nums[slow]
位置。 -
然后,
nums[slow] = nums[fast]
就是将当前的唯一元素nums[fast]
复制到nums[slow]
位置,从而保持nums[0..slow]
中的元素都是唯一的。
时间复杂度以及空间复杂度:
时间; O(n) n是数组长度,只遍历一次数组
空间; O(1),只用了常数空间
Q2题目链接:83. Remove Duplicates from Sorted List
leetcode 83
看到题目的第一思路:
和上题类似操作,只不过一个在数组里面去重,一个在链表里面,所以用同样快慢指针的方法,只不过把数组赋值操作变成操作指针而已
代码随想录之后的想法和总结:
Q: slow.next = None的作用?
当 fast
指针遍历完链表后,slow
指针停留在链表的最后一个不重复节点上。
-
然而,链表中的
slow
节点之后可能还会有重复的元素。为了确保链表最后的节点没有多余的重复元素,slow.next = None
的操作会断开与任何后续重复节点的连接。具体来说,slow.next
指向None
,确保链表的最后部分不包含重复节点。
Q: return head的作用?
-
这段代码是修改链表的原地操作,最后返回的是链表的头节点
head
,并且已经通过slow
指针去除了重复元素。 -
由于是原地修改链表,
head
作为链表的头节点被返回,这样调用者就能获得修改后的链表。
时间复杂度以及空间复杂度:
时间:O(n)只有遍历链表,n是链表的长度
空间:O(1) 只有原地修改链表
Q3题目链接: https://leetcode.com/problems/remove-element/
leetcode 27
看到题目的第一思路:
给数组去重,也可以用同样的双指针思路
代码随想录之后的想法和总结:
注意:这道题和前面两题思路不一样之处:首先更新数值,更新数组,然后再移动慢指针到下一位,因为为了保存有效元素,及时更新数组,防止移动指针造成错误指向
⚠️为什么先赋值再增加 slow?
-
保证元素在正确的位置:
-
slow
指针始终指向下一个可填充不等于val
的位置。通过nums[slow] = nums[fast]
,我们将符合条件的元素放到正确的位置,然后才更新slow
,使得slow
继续指向下一个位置。 -
如果先增加
slow
再赋值,会导致nums[slow]
始终指向错误的地方,可能会覆盖掉已经存在的有效元素,造成数据丢失。
-
-
保持数组的前部分不包含
val
:-
nums[0..slow-1]
中的元素都是不等于val
的,且它们的顺序不被破坏。因此,返回slow
就是结果数组的长度,表明有多少个不等于val
的元素。
-
Q:为什么return slow?
A:slow
的值表示数组中不等于 val
的元素个数,因为 slow
是用来追踪有效元素的,最终它指向的就是下一个可以放置有效元素的位置,所以 slow
的值即为结果数组的长度。
时间复杂度以及空间复杂度:
时间:O(n)
空间:O(1)
Q4题目链接:https://leetcode.com/problems/move-zeroes/
leetcode 283
看到题目的第一思路:
和前一题思路类似
代码随想录之后的想法和总结:
题目让我们将所有 0 移到最后,其实就相当于移除 nums
中的所有 0,然后再把后面的元素都赋值为 0
所以在首先利用前一题的方法,去除数组里面所有0,返回一个不含0的数组,然后把剩下的数组长度里面的元素 全部赋值为0
时间复杂度以及空间复杂度:
时间;O(n)
空间:O(1)
Q4题目链接:https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/
leetcode 167 两数之和
看到题目的第一思路:
可以参考二分查找,因为是有序数组
代码随想录之后的想法和总结:
Q:为什么之前二分法用的是left <= right,而这里用left = right?
A 因为之前二分法寻找的是单个数字是否等于target,而非两数之和,当left= right时候,只有一个数字,无法取出两数去验证两数之和,也就失去了意义
Q: 为什么这里是right = right-1 而不是二分查找的right = mid-1?
A: 这道题 每次只移动一边指针 把 right -= 1
(向左)移动一步,让总和变小。
二分法:为了缩小搜索区间,因为二分查找不只移动一步,它是:
-
每次都把当前区间
[left, right]
分成左右两半 -
根据
mid
值来判断,直接丢弃一半
Q:学习了二分查找的思路,但是这道题和二分查找有什么区别?
-
二分查找是“一次掰成两半”去判断某个数是不是答案
-
Two Sum 的双指针更像是“试着配对,看能不能凑成目标和”,每次只稍微调一下方向
总结:Two Sum 的双指针 不是严格意义上的二分查找,但借鉴了“二分查找”的核心思维:利用排序 + 排除无效解,从而高效地缩小搜索范围。
时间复杂度以及空间复杂度:
时间:O(n) 每个元素最多遍历一次
空间:O(1) 只用了两个指针变量
Q5题目链接:https://leetcode.com/problems/longest-palindromic-substring/
leetcode 5 最长回文字串
看到题目的第一思路:
-
回文串是正着和反着读都一样的字符串,比如
"aba"
、"racecar"
。 -
每个字符(或相邻两个字符)都可能是一个回文串的“中心”,你可以从中心向左右两边扩展,直到两边字符不相等。
代码随想录之后的想法和总结:
中心扩展法:对于每个可能的回文中心,包括奇数和偶数回文中心,用双指针左l,右r从中心向两边扩展,直到字符串不匹配越界,
“同步考虑奇数和偶数长度的回文串,找出当前能得到的最长回文,并更新结果。”
Q :l,r 和i,i 的函数区别?
刚进入函数的时候,l
和 r
就是你传入的 i
和 i
,它们初始值完全一样。
这道题和之前其他双指针唯一的不同:这道题从中间开始向两边移动指针,而不是从外侧向内移动
时间复杂度以及空间复杂度:
时间O(n^2) -平方级别 因为是外层和内层两个嵌套循环
空间: O(1) 因为没有用额外空间 只用了几个变量res,s1,s2来保存结果
今日收获,学习时长:
双指针用途多多,快慢指针和左右指针都是常见思路,在数组相关问题要经常联想到
注意 在回文串中运用到,除了之前从外侧向内移动的双指针技巧以外,也有从中心向外侧移动的中心扩展法双指针,灵活变通