双指针
双指针包括快慢指针和反向指针;
快慢指针:快指针走在前面,慢指针走在后面;快指针用于遍历,慢指针用于和快指针指向的对象进行对比,当满足条件时慢指针才向前移动
反向指针:一个指针指向数组的头,另一个指向数组尾,两个指针交替前进,当满足某个条件时停下来,接着继续前进,直到两个指针相遇,算法结束
滑动窗口https://blog.csdn.net/u010569893/article/details/105266952 实际上也是双指针的应用,滑动窗口的右指针不断扩大窗口的范围,当窗口内的对象符合某个条件时,进行统计或者某种操作;然后左指针收缩窗口来打破条件,让右指针继续扩大窗口;这里滑动窗口的题目就不再展开了
-
392. 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
思路:使用快慢指针;
快指针指向长字符串t,快指针不断前移;
慢指针指向短字符串s, 当慢指针的字符 跟快指针的字符 相同时,慢指针前移;
当慢指针在快指针之前或者同时遍历完字符串时,返回True; 否者返回False
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
if not s:
return True
i, j = 0, 0 # i:慢指针,j:快指针
while j<len(t):
c = t[j]
if s[i] == c:
if i == len(s)-1:
return True
i += 1
j+=1
return False
2.两个数组的交集
给定两个数组,编写一个函数来计算它们的交集
思路:使用快慢指针;
快指针指向长字符串t,快指针不断前移;
慢指针指向短字符串s, 当快指针指向的字符在短字符串中时,慢指针前移;
当慢指针在快指针之前或者同时遍历完字符串时,返回True; 否者返回False
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
if not nums1 or not nums2:
return []
nums1 = list(set(nums1))
nums2 = list(set(nums2))
res = []
#nums1:短数组;nums2:长数组
if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1
i, j = 0, 0 # i:慢指针指向短数组, j:快指针,指向长数组
while j < len(nums2):
num = nums2[j]
if num in nums1:
res.append(num)
if i == len(nums1)-1:
break
i+=1
j+=1
return res
-
三数之和的多种可能
给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。
思路:首先将数组排序;
使用三个指针,第一个指针i用于遍历数组,第二个指针j(左指针)指向i后的第一个元素,第三个指针j(右指针)执行i后的最后一个元素
当j和k指向的元素之和大于target时,k左移;小于target时,j右移;当元素之和等于target时,进行统计;
由于数组中可能存在重复元素,根据数学组合公式,来进行计算
当左指针和右指针相遇时,某次遍历结束,进行下一次遍历
class Solution: def threeSumMulti(self, A: List[int], target: int) -> int: MOD = 10**9 + 7 ans = 0 A.sort() for i, x in enumerate(A): T = target - A[i] j, k = i+1, len(A) - 1 while j < k: if A[j] + A[k] < T: j += 1 elif A[j] + A[k] > T: k -= 1 elif A[j] != A[k]: # We have A[j] + A[k] == T. left = right = 1 while j + 1 < k and A[j] == A[j+1]: left += 1 j += 1 while k - 1 > j and A[k] == A[k-1]: right += 1 k -= 1 ans += left * right ans %= MOD j += 1 k -= 1 else: # M = k - j + 1 # We contributed M * (M-1) / 2 pairs. ans += (k-j+1) * (k-j) / 2 ans %= MOD break
-
颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
思路:使用三个指针;第一个指针curr进行遍历, 第二个指针p0(左指针)指向最左边, 第三个指针p2(右指针)指向最右边;
当curr指向的当前数=2时,将当前数跟右指针指向的元素互换,然后右指针左移一位;
当当前数=0时,将0跟左指针指向的元素互换,然后左指针右移一位;curr继续左移
当当前数=1时,不做处理,curr继续左移
class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ if len(nums)<=1: return p0=curr=0 #p0:0的起始位置 curr:当前指针 p2 = len(nums)-1 #2的起始位置 while curr<=p2: if nums[curr]==0: nums[curr],nums[p0] = nums[p0], nums[curr] curr+=1 p0+=1 elif nums[curr]==2: nums[curr], nums[p2] = nums[p2], nums[curr] p2-=1 elif nums[curr]==1: curr+=1
-
80. 删除排序数组中的重复项 II
思路:快指针用于遍历,慢指针用于更新数组
class Solution: def removeDuplicates(self, nums: List[int]) -> int: if len(nums)<=2: return len(nums) count = 0 i, j = 1, 1 # 快、慢指针 while i<len(nums): if nums[i]==nums[i-1]: count+=1 if count<=1: nums[j] = nums[i] j+=1 i+=1 else: nums[j] = nums[i] j+=1 i+=1 count = 0 return len(nums[:j])
-
寻找重复数
思路:根据鸽巢原理(抽屉原理),当存在重复数时,必定有一个数字的个数大于1
暴力方法:从1-n依次统计每个数出现的次数,当某个数出现的次数大于1时,该数就是重复数
我们知道该数组中是一个1-n是有序数组,可以用二分法来统计,左指针指向1,右指针指向n
class Solution: def findDuplicate(self, nums: List[int]) -> int: size = len(nums)-1 l = 1 r = size while l<r: m = (l+r)//2 cnt = 0 for num in nums: if num <= m: cnt +=1 if cnt > m: r = m else: l = m+1 return l
-
344. 反转字符串
class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ if len(s)<=1: return l = 0 r = len(s)-1 while l<r: s[l], s[r] = s[r], s[l] l+=1 r-=1
-
567. 字符串的排列
思路:使用i和j来构造一个滑动窗口,其中i用于遍历,j用于构造一个长度为len(s1)的滑动窗口
遍历s2,当有字符在s1中时,比较排序后的滑动窗口内的字符串是否跟排序后的s1相同,相同的话,返回True,反之继续遍历
class Solution: def checkInclusion(self, s1: str, s2: str) -> bool: s1 = sorted(s1) for i in range(len(s2)): if s2[i] not in s1: continue j = i+len(s1)-1 if j > len(s2)-1: return False else: sub_s2 = sorted(s2[i:j+1]) if sub_s2 == s1: return True return False
-
167. 两数之和 II - 输入有序数组
思路:反向指针,当两数之和==target时,返回下标;当>target,右指针左移;当<target,左指针右移
class Solution: def twoSum(self, numbers: List[int], target: int) -> List[int]: nums = numbers l = 0 r = len(nums)-1 while l<r: s = nums[l]+nums[r] if s== target: return l+1,r+1 elif target>s: l+=1 elif target<s: r-=1
-
141. 环形链表
思路:使用快慢指针,当存在环时,快指针永远不会走到尽头,慢指针总会追上快指针;反之,当快指针走到尽头时,慢指针追不上快指针
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def hasCycle(self, head: ListNode) -> bool: if not head: return False slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: return True break return False
-
259. 较小的三数之和
思路:使用3指针,一个指针用于遍历,另外两个是左右指针
class Solution: def threeSumSmaller(self, nums: List[int], target: int) -> int: if len(nums) < 3: # 处理边界条件 return 0 nums.sort() ans = 0 for i in range(len(nums) - 2): # 注意i,j,k三个指针不能重合 left = i + 1 right = len(nums) - 1 while left < right: # 如果left和right之和小于target-nums[i],left右移 if nums[left] + nums[right] < target - nums[i]: ans += right - left left += 1 # 如果left和right之和大于target-nums[i],right左移 else: right -= 1 return ans
-