双指针可以分为以下几种:
- 双撞指针,分别从头尾开始往中间遍历,比如接雨水、盛水最多容器,反转字符串等
- 访问同一个元素的同向指针。这种类型的双指针常常会结合滑动窗口法使用,快指针先右移增大窗口,慢指针右移缩小窗口。如最小覆盖子串、最小无重复子数组
- 访问不同元素的同向指针。比如合并两个有序数组中,定义双指针分别指向两个数组的尾部开始遍历
合并两个有序数组
判断是否为回文字符串
合并区间
最小覆盖子串
反转字符串
最长无重复子数组
盛水最多的容器
接雨水问题
合并两个有序数组【BM87】
给出一个有序的整数数组 A 和有序的整数数组 B ,请将数组 B 合并到数组 A 中,变成一个有序的升序数组
- 双指针i、j,分别指向A和B最后一个有序整数,定义一个指针p指向A末尾
- 向A后部空白区域填补元素,将AB中较大元素填到p指针处
- 时间复杂度O(m+n),空间复杂度O(1),因为A数组本来存在,没有新开辟数组
- 简易版空间复杂度O(m+n)、新开辟一个数组存储排序后的元素,再将其复制给A
def merge(self , A, m, B, n):
# write code here
i = m-1
j = n-1
p = m+n-1
while i >= 0 and j >= 0:
if A[i] < B[j]:
A[p] = B[j]
j -= 1
else:
A[p] = A[i]
i -= 1
p -= 1
while j >= 0:
A[j] = B[j]
j -= 1
return A
判断是否为回文字符串 【BM88】
给定一个长度为 n 的字符串,请编写一个函数判断该字符串是否回文。如果是回文请返回true,否则返回false。
字符串回文指该字符串正序与其逆序逐字符一致。
思路:双指针,分别从数组头尾开始往中间扫,如果两个指针指向的元素相等,则同时往中间移动;否则说明不是回文字符串,返回False。
class Solution:
def judge(self , str: str) -> bool:
# write code here
i = 0
j = len(str)-1
while i <= j:
if str[i] == str[j]:
i += 1
j -= 1
else:
return False
return True
合并区间【BM89】
给出一组区间,请合并所有重叠的区间。请保证合并后的区间按区间起点升序排列。
数据范围:区间组数0≤n≤2×10^5,区间内 的值都满足0≤val≤2×10^5
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
进阶:空间复杂度 O(val),时间复杂度O(val)
输入:[[10,30],[20,60],[80,100],[150,180]]
输出:[[10,60],[80,100],[150,180]]
思路:先将区间按照start进行排序,然后进行两两合并
- res存放合并好的区间,每次取res中最后一个元素,将其与待合并区间的第一个区间进行合并
- left和right区间存在三种情况
- 两个区间不相交,不需要合并,即left.end > right.start
- right可以合并到left中,即left.end > right.end
- right可以增大left区间的长度,即right.end > left.end
- 因为先做了排序,因此right的左边界要么包含在left中,要么比left的右边界还要大,因此只需要考虑以上三种情况,即可完成合并
# class Interval:
# def __init__(self, a=0, b=0):
# self.start = a
# self.end = b
from functools import cmp_to_key
class Solution:
def merge(self , intervals: List[Interval]) -> List[Interval]:
# write code here
if len(intervals) <= 1:
return intervals
i = 1
intervals.sort(key=cmp_to_key(lambda a,b:1 if a.start > b.start else -1))
res = []
res.append(intervals[0])
while i < len(intervals):
left = res.pop(-1)
right = intervals[i]
if left.end < right.start:
res.append(left)
res.append(right)
elif left.end < right.end:
l = left.start
r = right.end
res.append(Interval(l,r))
else:
res.append(left)
i += 1
return res
盛水最多的容器 【BM93】
给定一个数组height,长度为n,每个数代表坐标轴中的一个点的高度,height[i]是在第i点的高度,请问,从中选2个高度与x轴组成的容器最多能容纳多少水
1.你不能倾斜容器
2.当n小于2时,视为不能形成容器,请返回0
3.数据保证能容纳最多的水不会超过整形范围,即不会超过2^31-1
数据范围:
0<=height.length<=10^50
0<=height[i]<=10^40
class Solution:
def maxArea(self , height: List[int]) -> int:
i = 0
j = len(height) - 1
s = 0
while i <= j:
s = max(s,min(height[i],height[j])*(j-i))
if height[i] <= height[j]:
i += 1
else:
j -= 1
return s
接雨水
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个柱子高度图,计算按此排列的柱子,下雨之后能接多少雨水。(数组以外的区域高度视为0)
数据范围:数组长度 0≤n≤2×105,数组中每个值满足0<*v**a**l*≤109 ,保证返回结果满足 0 ≤ val ≤10^9
要求:时间复杂度 O(n)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4DVJH8bi-1651470799158)(常考编程题.assets/image-20220501144535500.png)]
【输入】[3,1,2,5,2,4]
【输出】5
题解:
双撞指针,雨水的最大容量取决于容器的最短边。
- 记录容器的边界l和r
- 从最短边开始向中间遍历,如果当前高度小于边界值,则承受雨水的容量为 【边界值—高度值】
- 当前高度大于边界值,说明容器可以分割为两个,记录新的边界值,重新往中间遍历
class Solution:
def maxWater(self , arr: List[int]) -> int:
if not arr:
return None
i = 0
j = len(arr)-1
l = arr[i]
r = arr[j]
res = 0
while i < j:
if l <= r:
i += 1
if arr[i] <= l:
res += l - arr[i]
else:
l = arr[i]
else:
j -= 1
if arr[j] <= r:
res += r - arr[j]
else:
r = arr[j]
print(res)
return res