双指针
第一题:
125. 验证回文串
https://leetcode.cn/problems/valid-palindrome/
题目内容
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
示例 1:
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。
提示:
1 <= s.length <= 2 * 105s仅由可打印的 ASCII 字符组成
思路
两个指针初始化,一根指向开头一根指向结尾。
若左指针对应内容不为单词,则右移1;右指针同理,若对应内容不为单词,则左移1
若开头等于结尾,则左边右移1,右边左移1。
结束判定:当左指针<右指针时
代码
class Solution:
def isPalindrome(self, s: str) -> bool:
# 先全部转小写
s = s.lower()
# 双指针
i = 0
j = len(s)-1
while i<j:
# 若当前指向内容不为字母,则移动指针
if not s[i].isalnum():
i += 1
continue
if not s[j].isalnum():
j -= 1
continue
if s[i] != s[j]:
return False
i += 1
j -= 1
return True
第二题:
392. 判断子序列
https://leetcode.cn/problems/is-subsequence/
题目内容
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true
示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false
提示:
0 <= s.length <= 1000 <= t.length <= 10^4- 两个字符串都只由小写字符组成。
思路
两个字符串s和t,分别用一根指针i和j。若i指向内容等于j指向内容,则i右移1,j右移1;若i指向内容不等于j指向内容,则i不动,j右移1。因此。分析发现,不管怎么样j都会右移1,而i不一定。
那么最终结束的判定条件就是i能否等于len(s),若等于,则True,否则False
附:也可以使用for循环的方式遍历t,然而时间复杂度更高,效率不及双指针。
代码
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
i = 0
j = 0
lengs = len(s)
lengt = len(t)
# while 循环判定范围
while i<=lengs-1 and j<=lengt-1:
if s[i] == t[j]:
i += 1
# j 指针不管怎么样都要右移
j += 1
# 结束条件
if i == lengs:
return True
else:
return False
“进阶”思路
目前的版本(双指针法)的时间复杂度是 O(∣s∣+∣t∣),如果有 K 个 s需要匹配,总时间复杂度就是 O(K⋅∣t∣)。当 K 很大且 t 很长时,每次都要遍历一遍 t,效率就很低了。
可以先对 t 进行一次预处理,建立一个“索引”。再用二分查找匹配,若能找到则更新位置,找不到则返回False
第三题
167. 两数之和 II - 输入有序数组
https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/
题目内容
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
提示:
2 <= numbers.length <= 3 * 104-1000 <= numbers[i] <= 1000numbers按 非递减顺序 排列-1000 <= target <= 1000- 仅存在一个有效答案
思路
一个数组,两个指针,分别指向头和尾,因为数组提前说好了,非递减顺序排列,且只对应唯一解。也就是说,右边的数字一定是大于等于左边的数字的。因此,若当前值>目标值,右指针左移1,同理,若当前值<目标值,那么左指针右移1。
代码
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 0
j = len(numbers) - 1
while i < j:
if numbers[i] + numbers[j] < target:
i+=1
elif numbers[i] + numbers[j] > target:
j-=1
else:
return [i+1,j+1]
第四题
11. 盛最多水的容器
https://leetcode.cn/problems/container-with-most-water/
题目内容
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
n == height.length2 <= n <= 1050 <= height[i] <= 104
思路
一个数组,两个指针,分别指向头和尾。
能盛多少水,就看长 * 宽。长就是两个指针对应的内容的最小值min(height),宽就是两根指针的距离(j-i),更新条件就是判断左右的高低,左边低就往右移动(因为宽边小了,想要最终的面积更大,必须移动短的那一根,如果短的板不动的话,取到的水永远不会比上次多),同理右边低就往左移,知道i>=j结束。最终返回max_ans即为最大值。
代码
class Solution:
def maxArea(self, height: List[int]) -> int:
i = 0
j = len(height)-1
ans = 0
while i<j:
# 最终的面积
ans = max(ans, min(height[i],height[j]) * (j-i))
if height[i]<height[j]:
i+=1
else:
j-=1
return ans
第五题
15. 三数之和
https://leetcode.cn/problems/3sum/
题目内容
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000-105 <= nums[i] <= 105
思路
先对整个列表从小到大排序,然后固定一个数i,对于剩下的两个数,双指针分别指向头j(i+1)和尾k(n-1)
先对i进行去重(第一次尝试写的时候犯的错,漏了 i),接着进行计算,若三数之和小于0,则左指针右移,因为j的左侧一定都比当前的值小;同理若三数之和大于0,则右指针左移,因为k的右侧一定都比当前的值大;若等于0,则记录答案。然后左右指针各自均移动。
值得注意的是(第一次尝试写的时候犯的错),在记录完答案并移动左右指针之后,还要对j、k去重。且这一步必须在记录完答案之后进行。否则会导致漏掉某些解。即去重逻辑应该是在找到一个有效三元组之后,为了寻找下一个可能解时才执行。在没有确定当前组合是否有效之前,不应该随意跳过指针。
代码
class Solution:
def threeSum(self, nums: list[int]) -> list[list[int]]:
# 先从小到大排序,便于后续使用双指针
newnums = sorted(nums)
n = len(nums)
ans = []
for i in range(n-2):
# i 去重
if i>0 and newnums[i] == newnums[i-1]:
continue
# 双指针分别指向头和尾
j = i + 1
k = n - 1
while j<k:
# 结果小于0,则左指针右移
if newnums[i]+newnums[j]+newnums[k]<0:
j+=1
# 结果大于0,则右指针左移
elif newnums[i]+newnums[j]+newnums[k]>0:
k-=1
else:
# 记录结果
ans.append([newnums[i], newnums[j], newnums[k]])
j+=1
k-=1
# 进一步j、k去重
while j<k and newnums[j] == newnums[j-1]:
j+=1
while j<k and newnums[k] == newnums[k+1]:
k-=1
return ans

395

被折叠的 条评论
为什么被折叠?



