双指针(2)
- 回文字符串
- 归并两个有序数组
- 判断链表是否存在环(更新。。。)
- 最长子序列(更新。。。)
1.回文字符串
使用双指针很容易判断一个字符串是不是回文,本题的关键是删除一个字符后判断是不是回文。在双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,可以删除左指针指向的字符,也可以删除右指针指向的字符,再判断删除后的字符串是不是回文。其实我们会发现,删除后字符串的范围为(low+1,high)或者(low,high-1),只要这两个子串任意一个是回文,那么结果就是回文,否则就不是。
class Solution:
def validPalindrome(self, s: str) -> bool:
if s==s[::-1]:
return True
low=0
high=len(s)-1
while low<high:
if s[low]!=s[high]:
a=s[low+1:high+1]
b=s[low:high]
return a==a[::-1] or b==b[::-1]
else:
low+=1
high-=1
时间复杂度O(n):n是字符串的长度。
空间复杂度O(n):判断是否回文使用了 [::-1] 翻转形成了新字符串。
2.归并两个有序数组
方法一:双指针/从前往后
对于有序数组,最直接的算法就是将指针p1置为nums1的开头,p2置为nums2的开头,在每一步将最小值放入输出数组中。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
#双指针,从前往后
nums1_copy=nums1[:m]
nums1[:]=[]
p1=0
p2=0
while p1<m and p2<n:
if nums1_copy[p1]<nums2[p2]:
nums1.append(nums1_copy[p1])
p1+=1
else:
nums1.append(nums2[p2])
p2+=1
if p1<m:
nums1[p1+p2:]=nums1_copy[p1:]
if p2<n:
nums1[p1+p2:]=nums2[p2:]
时间复杂度O(m+n)
空间复杂度O(m):由于nums1是用于输出的数组,需要将nums1的前m个元素放在其他地方,也就是O(m)的空间复杂度
方法二:双指针/从后往前
刚才的方法已经取得了最优的时间复杂度O(m+n),但需要额外的空间。改进:从后往前遍历,因为nums1的空间都集中在后面,节省空间。p1,p2分别指向nums1,nums2的有数字尾部,p指向nums1的最尾部,一边遍历(比较大小)一边填充(将大的填充到p)。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
#双指针/从后往前
p1=m-1
p2=n-1
p=m+n-1
while p1>=0 and p2>=0:
if nums1[p1]<nums2[p2]:
nums1[p]=nums2[p2]
p2-=1
else:
nums1[p]=nums1[p1]
p1-=1
p-=1
nums1[:p2+1]=nums2[:p2+1]#nums2中还有数据没有拷贝完,将其直接拷贝在nums1前面
时间复杂度O(m+n)
空间复杂度O(1)