文章目录
1. 双指针概述
双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。双指针也可以扩展为多个数组的多个指针。
2. 两数之和问题
2.1 LeetCode-No.167-Medium
解题思路:
总体目的: 找到递增数组中和为
t
a
r
g
e
t
target
target的两个数。
双指针: 左指针
l
l
l,从前向后遍历数组;右指针
r
r
r,从后向前遍历数组。
总体流程: 对输入数组进行一次遍历,双指针的移动规则如下:
l
+
r
{
>
t
a
r
g
e
t
r
−
−
<
t
a
r
g
e
t
l
+
+
=
t
a
r
g
e
t
b
r
e
a
k
(2.1.1)
l+r \begin{cases} \text{>}target& r--\\ \text{<} target& l++\\ \text{=}target& break \end {cases}\tag{2.1.1}
l+r⎩⎪⎨⎪⎧>target<target=targetr−−l++break(2.1.1)
算法终止条件: 由于存在且仅存在一个有效解,则当满足
l
+
r
=
t
a
r
g
e
t
l+r=target
l+r=target时算法即终止。
2.2 LeetCode-No.633-Medium
解题思路:
总体目的: 给出
c
c
c,验证是否存在
a
2
+
b
2
=
c
a^2+b^2=c
a2+b2=c的
a
a
a和
b
b
b 。
双指针: 左指针
l
l
l表示
a
a
a(较小的数),从小向大遍历;右指针
r
r
r表示
b
b
b(较大的数),从大向小遍历。应初始化双指针为最小/最大值,根据数学知识可知:
l
=
0
,
r
=
l
o
n
g
(
c
)
l=0,r=long(\sqrt c)
l=0,r=long(c)。
总体流程: 进行一次遍历,若两数平方和小于
c
c
c,
l
+
+
l++
l++;若两数平方和大于
c
c
c,
r
−
−
r--
r−−;若两数平方和等于
c
c
c,直接返回。
算法终止条件: 找到符合要求的
l
l
l和
r
r
r或
l
>
r
l>r
l>r(找不到符合要求的数)时算法即终止。
3. 归并两个有序数组问题
3.1 LeetCode-No.88-Easy
解题思路:
总体目的: 归并
n
u
m
s
2
nums2
nums2到
n
u
m
s
1
nums1
nums1中,两个均为非递减数组。
双指针: 由于
n
u
m
s
1
nums1
nums1中待插入的额外空间位于后半部分,应从两个数组的末尾从后向前将元素按从大到小的顺序依次插入到
n
u
m
s
1
nums1
nums1中。因此,双指针分别为指向数组
n
u
m
s
1
nums1
nums1末尾的指针
m
m
m,指向数组
n
u
m
2
num2
num2末尾的指针
n
n
n,此外还需要一个初始指向
m
+
n
−
1
m+n-1
m+n−1处的当前插入位置指针
p
o
s
pos
pos。
总体流程: 插入时
m
m
m和
n
n
n不断进行比较并插入到
p
o
s
pos
pos处,比较的规则如下:
{
n
u
m
s
1
[
m
]
>
n
u
m
s
2
[
n
]
m
−
−
n
u
m
s
1
[
m
]
≤
n
u
m
s
2
[
n
]
n
−
−
,
p
o
s
−
−
(3.1.1)
\begin{cases} nums1[m]>nums2[n]& m--\\ nums1[m]\le nums2[n]& n-- \end{cases},pos--\tag{3.1.1}
{nums1[m]>nums2[n]nums1[m]≤nums2[n]m−−n−−,pos−−(3.1.1)
m
m
m或
n
n
n为0时则终止比较。若此时
m
≠
0
m\neq0
m=0,无需额外处理,因为
n
u
m
s
1
nums1
nums1中的剩余元素已经位于
n
u
m
s
1
nums1
nums1的开头并有序,若此时
n
≠
0
n\neq0
n=0,需将
n
u
m
s
2
nums2
nums2中的剩余元素从大到小依次插入到
n
u
m
s
1
nums1
nums1中。
算法终止条件: 总体流程结束后算法即终止,本算法总体而言是对两个数组进行了一次从后向前的遍历。由于本题不允许开辟新的数组以存储两个数组的归并结果,遍历的方向只能是从后向前,否则从前向后按元素从小到大的顺序归并也可以。
3.2 LeetCode-No.524-Medium
解题思路:
总体目的: 判断字符串
s
s
s是否可以通过删除某些字母得到字符串
d
d
d(两字符串均由小写英文字母组成)。
双指针: 指向字符串
s
s
s的指针
p
1
p1
p1,指向字符串
d
d
d的指针
p
2
p2
p2,两个指针均按从前向后的顺序遍历各自指向的字符串。另外,设置一个辅助变量
c
n
t
cnt
cnt记录当前已匹配的字符数。双指针移动规则如下:
总体流程: 数组
d
i
c
t
i
o
n
a
r
y
dictionary
dictionary中有若干个字符串
d
d
d,采用双指针依次将字符串
s
s
s和字符串
d
d
d匹配,如果匹配成功,将字符串
d
d
d存入一个List容器中。待所有匹配完成后,按题目要求对List容器进行排序并输出。
算法终止条件: 所有匹配完成后算法即终止,若匹配后List容器中没有元素,说明数组
d
i
c
t
i
o
n
a
r
y
dictionary
dictionary中没有能匹配上的字符串
d
d
d。
4. 快慢指针问题
4.1 LeetCode-No.142-Medium
解题思路:
总体目的: 判断链表是否有环路并寻找环路的入口节点。
双指针: 采用快慢指针来解决环路问题。设快指针
f
a
s
t
fast
fast一次走两步,慢指针
s
l
o
w
slow
slow一次走一步。对于无环路的情况,显然,快指针首先走到链表的结尾节点,快慢指针永远不会相遇。对于有环路的情况,设链表的首节点到环路入口节点的距离为
x
x
x,环路的入口节点为
p
p
p,环路长度为
l
e
n
len
len,由于快指针的速度快,当慢指针走到入口节点
p
p
p时,快指针已经在环路中走了一段距离,此时的情况如下:
快指针与慢指针的距离 | 再走几次快慢指针相遇 |
---|---|
0 | 0或 n ∗ l e n n*len n∗len |
1 | 1 |
2 | 2 |
… | … |
m m m | m m m |
… | … |
l e n − 1 len-1 len−1 | l e n − 1 len-1 len−1 |
可知在有环路的情况下快慢指针在环路中必定相遇,设相遇节点为
q
q
q。慢指针走到入口节点
p
p
p时,设快指针与慢指针的距离为
m
m
m(同时也是入口节点
p
p
p与相遇节点
q
q
q的距离,因为慢指针从入口节点
p
p
p处再走
m
m
m步即和快指针相遇),显然:
0
≤
m
≤
l
e
n
−
1
0\le m \le len-1
0≤m≤len−1,因此还可以得知:当慢指针走到入口节点
p
p
p处时,快慢指针相遇最多只要走一圈。
当快慢指针第一次相遇时,慢指针走过的距离为:
s
=
x
+
m
s=x+m
s=x+m,快指针走过的距离为:
2
s
=
x
+
(
l
e
n
−
m
)
+
n
∗
l
e
n
+
2
∗
m
=
x
+
m
+
(
n
+
1
)
∗
l
e
n
=
x
+
m
+
δ
2s=x+(len-m)+n*len+2*m=x+m+(n+1)*len=x+m+\delta
2s=x+(len−m)+n∗len+2∗m=x+m+(n+1)∗len=x+m+δ,其中
l
e
n
−
m
len-m
len−m是快指针与入口节点
p
p
p的距离(同时也是相遇节点
q
q
q与入口节点
p
p
p的距离),代入公式可得:
x
=
−
m
+
δ
=
(
l
e
n
−
m
)
+
n
∗
l
e
n
x=-m+\delta=(len-m)+n*len
x=−m+δ=(len−m)+n∗len,则可知:从相遇节点
q
q
q再走
x
x
x步,即可回到入口节点
p
p
p处。
总体流程: 采用快慢节点找到环路中的相遇节点
q
q
q,将快指针放至链表开头并改为一次走一步,则当快指针从链表开头走
x
x
x步、慢指针从相遇节点
q
q
q处走
x
x
x步时,快慢节点将在开始节点
p
p
p处相遇,从而找出开始节点。
算法终止条件: 快指针走到链表的结尾节点或找出开始节点时算法即终止。
5 滑动窗口问题
5.1 LeetCode-No.76-Hard
解题思路:
总体目的: 寻找字符串
s
s
s中涵盖字符串
t
t
t中所有字符的最小子串
s
u
b
s
t
r
substr
substr。
双指针: 左指针
l
l
l用于寻找当前滑动窗口中最小子串的起始索引位置,右指针
r
r
r用于寻找当前滑动窗口恰好涵盖字符串
t
t
t中所有字符时的索引位置。
l
l
l和
r
r
r均初始化为0,最小子串
s
u
b
s
t
r
substr
substr的起始索引
m
i
n
l
minl
minl初始化为0,长度
l
e
n
len
len初始化为:
s
.
l
e
n
g
t
h
+
1
s.length+1
s.length+1(即设置为一个大于
s
u
b
s
t
r
substr
substr允许的最大长度的非法值)。双指针的滑动规则如下:
总体流程: 首先需要对字符串
t
t
t进行必要的统计,包括该字符串包含哪些字母、每个字母出现了几次,用于判断字符涵盖情况。采用双指针从左向右对进行
s
s
s一次滑动遍历,滑动遍历完成后,得到最小子串
s
u
b
s
t
r
substr
substr的长度
l
e
n
len
len和该子串的起始索引位置
m
i
n
l
minl
minl,可据此确定该子串。特别地,若
l
e
n
len
len始终为初始值,说明
s
s
s中不存在符合要求的最小子串。
算法终止条件: 遍历完成后算法即终止。尽管双指针的滑动中有两个循环,但两个指针全程最多只会对字符串
s
s
s各自遍历一次,因此时间复杂度仍为
O
(
n
)
O(n)
O(n)。