双指针 1. 思路 题目: 给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组 。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。27. 移除元素 - 力扣(LeetCode)
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
快指针:寻找新数组的元素,新数组就是不含有目标元素的数组。这个指针需要一直向前,同时寻找目标元素。 慢指针:指向更新 新数组下标的位置。指向需要更新的元素位置,如果没有找到目标元素,则跟着快指针一起向前移动。 2. 解法 Python
1 2 3 4 5 6 7 8 class Solution : def removeElement (self, nums: List [int ], val: int ) -> int : slowIdx, fastIdx = 0 , 0 for fastIdx in range (len (nums)): if nums[fastIdx] != val: nums[slowIdx] = nums[fastIdx] slowIdx += 1 return slowIdx
C++
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int removeElement (vector<int >& nums, int val) { int slowIdx = 0 , fastIdx = 0 ; for (fastIdx=0 ; fastIdx<nums.size (); fastIdx++){ if (nums[fastIdx] != val) nums[slowIdx++] = nums[fastIdx]; } return slowIdx; } };
3. 变种 (1) 题目: 给你一个 非严格递增排列 的数组 nums
,请你原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。26. 删除有序数组中的重复项 - 力扣(LeetCode)
思路: 还是使用常规的双指针解法。关键步骤是找到一个判断语句,使得 slow
慢指针移动,不同的题目 slow
移动的条件不同,只要找到符合移动的条件,就可以直接进行逻辑上的元素移动覆盖。最后返回 slow + 1
即为最后的数组的长度。
PS:一般情况下快指针是随着循环一直移动的。
Python
1 2 3 4 5 6 7 8 class Solution : def removeDuplicates (self, nums: List [int ] ) -> int : slow, fast = 0 , 0 for fast in range (len (nums)): if nums[slow] != nums[fast]: slow += 1 nums[slow] = nums[fast] return slow + 1
C++
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : int removeDuplicates (vector<int >& nums) { int slow = 0 , fast = 0 ; for (fast = 0 ; fast < nums.size (); fast++){ if (nums[slow] != nums[fast]){ nums[++slow] = nums[fast]; } } return slow + 1 ; } };
⁕⁕ (2) 题目: 给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true
。#
代表退格字符。844. 比较含退格的字符串 - 力扣(LeetCode)
思路: 一个字符是否会被删掉,只取决于该字符后面的退格符,而与该字符前面的退格符无关。因此当我们逆序地遍历字符串,就可以立即确定当前字符是否会被删掉。具体地,我们定义 skip 表示当前待删除的字符的数量。每次我们遍历到一个字符:若该字符为退格符,则我们需要多删除一个普通字符,我们让 skip 加 1;若该字符为普通字符:
PS:双指针移动,不一定两个指针都在同一个数组上,也可以在两个不同的数组上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution : def backspaceCompare (self, s: str , t: str ) -> bool : i, j = len (s) -1 , len (t) - 1 skipS = skipT = 0 while i>=0 or j>=0 : while i>=0 : if s[i] == "#" : skipS += 1 i -= 1 elif skipS > 0 : skipS -= 1 i -= 1 else : break while j>=0 : if t[j] == "#" : skipT += 1 j -= 1 elif skipT > 0 : skipT -= 1 j -= 1 else : break if i>=0 and j>= 0 : if s[i] != t[j]: return False elif i>=0 or j>=0 : return False i -= 1 j -= 1 return True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution {public : bool backspaceCompare (string s, string t) { int i = s.length () - 1 , j = t.length () - 1 ; int skipS = 0 , skipT = 0 ; while (i >= 0 || j >=0 ){ while (i>=0 ){ if (s[i--] == '#' ) skipS += 1 ; else if (skipS > 0 ){ skipS--; i--; } else break ; } while (j>=0 ){ if (t[j--] == '#' ) skipT += 1 ; else if (skipT > 0 ){ skipS--; j--; } else break ; } if (i>=0 && j>=0 ){ if (s[i] != t[j]) return false ; } else if (i >=0 || j >= 0 ) return false ; i--; j--; } return true ; } };
(3) 题目: 给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。977. 有序数组的平方 - 力扣(LeetCode)
思路:
使用两个指针分别指向位置 0 和 n−1,每次比较两个指针对应的数,选择较大的那个逆序放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况。 找到正数和负数的分界线,两个指针分别指向分界线和分界线的后一位,然后从中间往两端进行比较,将较小的放入新的数组中。 Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution : def sortedSquares (self, nums: List [int ] ) -> List [int ]: n = len (nums) ans = [0 ] * n i, j, idx = 0 , n-1 , n-1 while i<=j: if nums[i]**2 > nums[j]**2 : ans[idx] = nums[i]**2 i += 1 else : ans[idx] = nums[j]**2 j -= 1 idx -= 1 return ans
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : vector<int > sortedSquares (vector<int >& nums) { int i=0 , j=nums.size ()-1 , idx=nums.size ()-1 ; vector<int > ans (nums.size()) ; while (i<=j){ if (nums[i]*nums[i] > nums[j]*nums[j]){ ans[idx--] = nums[i] * nums[i]; i++; }else { ans[idx--] = nums[j] * nums[j]; j--; } } return ans; } };
⁕⁕ (4) 题目: 给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。11. 盛最多水的容器 - 力扣(LeetCode)
思路: 面对这个题目,如果没有经验,很难想到双指针。本题的容量大小,即为短板 * 当前水槽的长度
。设两指针 i , j ,指向的水槽板高度分别为h[i], h[j] ,此状态下水槽面积为S(i,j) 。由于可容纳水的高度由两板中的 短板
决定,因此可得如下 面积公式 :\[S(i,j)=min(h[i],h[j])×(j−i)\]
同时,无论长板还是短板向中间收窄一格,都会导致水槽底边宽度 -1:
若向内 移动短板 ,水槽的短板 min(h[i],h[j])
可能变大,因此下个水槽的面积 可能增大 。 若向内 移动长板 ,水槽的短板 min(h[i],h[j])
不变或变小,因此下个水槽的面积 一定变小 。 PS:为了减少时间,进行一下优化,当水槽的容量大小,大于max(height) * (j - i)
时,则说明,水槽的容量不会再有机会变大了。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution : def maxArea (self, height: List [int ] ) -> int : l = len (height) left, right = 0 , l - 1 ans = 0 maxh = max (height) while left <= right: if height[left] < height[right]: ans = max (ans, height[left] * (right-left)) left += 1 else : ans = max (ans, height[right] * (right-left)) right -= 1 if ans >= maxh * (right - left): break return ans
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public : int maxArea (vector<int >& height) { int left = 0 , right = height.size () - 1 , ans = 0 ; while (left <= right){ if (height[left] < height[right]){ ans = max (ans, (right - left) * height[left++]); }else { ans = max (ans, (right - left) * height[right--]); } } return ans; } };
(5) 题目: 给你一个长度为 n
的整数数组 nums
和 一个目标值 target
。请你从 nums
中选出三个整数,使它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在恰好一个解。16. 最接近的三数之和 - 力扣(LeetCode)
思路:
首先进行数组排序,时间复杂度 O(nlogn) 在数组 nums 中,进行遍历,每遍历一个值利用其下标i,形成一个固定值 nums[i] 再使用前指针指向 start = i + 1 处,后指针指向 end = nums.length - 1 处,也就是结尾处 根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans 同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end--,如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果 整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O(n2) 总时间复杂度:O(nlogn)+O(n2)=O(n2) Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def threeSumClosest (self, nums: List [int ], target: int ) -> int : nums = sorted (nums) ans = nums[0 ] + nums[1 ] + nums[2 ] for i in range (len (nums)): l, r = i + 1 , len (nums) - 1 while l < r: s = nums[l]+nums[r]+nums[i] if abs (target - s) < abs (target - ans): ans = s if s > target: r -= 1 elif s < target: l += 1 else : return ans return ans
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int threeSumClosest (vector<int >& nums, int target) { sort (nums.begin (), nums.end ()); int ans = nums[0 ] + nums[1 ] + nums[2 ]; for (int i=0 ; i<nums.size (); i++){ int l = i + 1 , r = nums.size () - 1 ; while (l < r){ int sum = nums[i] + nums[l] + nums[r]; if (abs (target - sum) < abs (target - ans)) ans = sum; if (sum < target) l++; else if (sum > target) r--; else return ans; } } return ans; } };