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来保存结果
今日收获,学习时长:
双指针用途多多,快慢指针和左右指针都是常见思路,在数组相关问题要经常联想到
注意 在回文串中运用到,除了之前从外侧向内移动的双指针技巧以外,也有从中心向外侧移动的中心扩展法双指针,灵活变通
