目录
双指针的题目一般分为2种,一种是类似龟兔赛跑的快慢指针,2个指针同时从头出发;另一种是类似左右指针的碰撞指针,2个指针分别从头尾出发,直到相撞
快慢指针/原地修改:快慢指针的2个指针从开始位置一起出发,fastp用于遍历数组中每个元素,slowp用于指向数组中待赋值的位置,并等待fastp寻找到符合要求的给其赋值,最后slowp指向的位置之前的就是满足题意的部分;如果不是原地修改,则2个指针也是从2个变量的各自位置出发,具体问题具体分析,没有统一的套路/模板
碰撞指针:左右指针分别指向数组的首尾位置,依次寻找满足条件的位置,并移动指针,一般来说这种题目都会涉及到反转/翻转字符串/数组
需要强调的是,虽然双指针有各种分类,但是万变不离其宗,本质上都是通过指针遍历的方式遍历数组/字符串,如果是快慢指针则1个指针遍历1个指针辅助记录满足条件的位置,如果是碰撞指针则2个指针都用来遍历数组/字符串(没有速度的概念),直到2个指针相遇
快慢指针
27 移除元素
- easy
- 题目描述
给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
- 解题思路
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slowp = 0
fastp = 0
for fastp in range(len(nums)):
# fastp会一直向后面寻找到不等于val的元素,此时slow指向等于val的位置
if nums[fastp] != val:
nums[slowp] = nums[fastp]
slowp += 1
# slow指向等于val的位置,因为返回长度,所以不需要-1
return slowp
26 删除排序数组中的重复项
- easy
- 题目描述
给你一个有序数组 nums
,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
- 解题思路
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) <= 1:
return len(nums)
slowp = 0
for fastp in range(1, len(nums)):
if nums[fastp] == nums[slowp]:
continue
else:
slowp += 1
nums[slowp] = nums[fastp]
return slowp + 1
283 移动零
- easy
- 题目描述
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
- 解题思路
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
fastp = 0
slowp = 0
for fastp in range(len(nums)):
if nums[fastp] == 0:
continue
else:
nums[slowp] = nums[fastp]
slowp += 1
# 最后把剩余部分更新为0
nums[slowp:len(nums)] = [0 for i in range(len(nums)-slowp)]
844 比较含退格的字符串
- easy
- 题目描述
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。
如果相等,返回 true ;否则,返回 false 。
注意:如果对空文本输入退格字符,文本继续为空。
- 解题思路
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
# 返回字符串对应的真实字符串
def trueString(s:str) -> str:
# slow记录真实字符串
slowp = 0
fastp = 0
# res = ""
# 原地修改
for fastp in range(len(s)):
if s[fastp] == '#':
slowp = slowp - 1 if slowp > 1 else 0
# res = s[:slowp]
else:
# 把slowp位置替换成fastp(同时保持字符串长度不变)
s = s[:slowp] + s[fastp] + s[slowp+1:]
slowp += 1
return s[:slowp]
print(trueString(s))
print(trueString(t))
return trueString(s) == trueString(t)
剑指offer05 替换空格
- easy
- 题目描述
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
- 解题思路
class Solution:
def replaceSpace(self, s: str) -> str:
# 双指针,统计空格数量,然后从后向前替换
count = 0
for i in s:
if i == " ":
count += 1
# 原始字符串长度
m = len(s)
# 替换以后字符串的长度
s += " "*(2*count)
# 字符串转成字符数组
res = list(s)
n = len(s)
i = m - 1
j = n - 1
while(i >= 0 and j > i):
if res[i] == " ":
res[j] = "0"
res[j-1] = "2"
res[j-2] = "%"
j -= 3
else:
res[j] = res[i]
j -= 1
i -= 1
return "".join(res)
碰撞指针
977 有序数组的平方
- easy
- 题目描述
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
- 解题思路
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 最大值一定产生于边界位置
leftp = 0
rightp = len(nums) - 1
res = [0 for i in range(len(nums))]
count = rightp
while(leftp <= rightp):
leftv = nums[leftp] * nums[leftp]
rightv = nums[rightp] * nums[rightp]
if rightv >= leftv:
res[count] = rightv
count -= 1
rightp -= 1
else:
res[count] = leftv
count -= 1
leftp += 1
return res
344 反转字符串
- easy
- 题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
- 解题思路
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
left = 0
right = len(s) - 1
while(left < right):
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
541 反转字符串Ⅱ
- easy
- 题目描述
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
- 解题思路
class Solution:
def reverseStr(self, s: str, k: int) -> str:
# 反转一个完整的字符串&确定每次反转的部分字符串
# 给定部分字符串,返回该字符串反转后的结果
def reverseAll(s: str) -> str:
left = 0
right = len(s) - 1
res = list(s)
while(left < right):
res[left], res[right] = res[right], res[left]
left += 1
right -= 1
return "".join(res)
p = 0
# 字符串转成列表,方便字符串的修改和赋值
res = list(s)
while(p!=len(s)):
if p+k < len(s):
res[p:p+k] = reverseAll(s[p:p+k])
else:
res[p:] = reverseAll(s[p:])
return "".join(res)
p = p+k*2
return "".join(res)
151 翻转字符串里的单词
先反转整体,再反转局部
- medium
- 题目描述
给你一个字符串 s ,逐个翻转字符串中的所有单词 。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
- 解题思路
class Solution:
def reverseWords(self, s: str) -> str:
# 先反转整个字符串,然后逐个反转单词
# 反转字符串
def reverseAll(s):
left = 0
right = len(s) - 1
res = list(s)
while(left < right):
res[left], res[right] = res[right], res[left]
left += 1
right -= 1
return res
# 去除原始字符串中的首尾空格
left = 0
while(s[left] == " "):
left += 1
right = len(s) - 1
while(s[right] == ""):
right -= 1
s_temp = reverseAll(s[left:right+1])
res = s_temp
# 反转逐个单词
left = 0
right = 0
# 标志前面是否已经找到一个单词,跳过长段空格
flag = 0
ans = ""
while(right != len(res)):
# 如果直接写if res[right]:这样是不对的,因为空格也会进入这个分支里
if res[right] != " ":
right += 1
flag = 1
# 碰到空格
else:
if flag == 1:
ans += "".join(reverseAll(res[left:right]))
ans += " "
# 前面的单词已经记录过了
flag = 0
right += 1
left = right
else:
right += 1
left = right
# 末尾没有空格了,所以最后一个单词需要手动添加
ans += "".join(reverseAll(res[left:right]))
return ans