题解-LeetCode-双指针

文章目录

  • 1. 双指针概述
  • 2. 两数之和问题
    • 2.1 LeetCode-No.167-Medium
    • 2.2 LeetCode-No.633-Medium
  • 3. 归并两个有序数组问题
    • 3.1 LeetCode-No.88-Easy
    • 3.2 LeetCode-No.524-Medium
  • 4. 快慢指针问题
    • 4.1 LeetCode-No.142-Medium
  • 5 滑动窗口问题
    • 5.1 LeetCode-No.76-Hard

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=targetrl++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+n1处的当前插入位置指针 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]mn,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记录当前已匹配的字符数。双指针移动规则如下:

N
Y
Y
N
Y,能匹配
N,不能匹配
开始
p1< s.length
&& p2< d.length?
cnt=d.length?
s[p1]=d[p2]?
p2++,cnt++
p1--
结束

总体流程: 数组 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 nlen
1 1
2 2
m m m m m m
l e n − 1 len-1 len1 l e n − 1 len-1 len1

可知在有环路的情况下快慢指针在环路中必定相遇,设相遇节点为 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 0mlen1,因此还可以得知:当慢指针走到入口节点 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+(lenm)+nlen+2m=x+m+(n+1)len=x+m+δ,其中 l e n − m len-m lenm是快指针与入口节点 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+δ=(lenm)+nlen,则可知:从相遇节点 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允许的最大长度的非法值)。双指针的滑动规则如下:

Y
N
N
Y
Y
N
开始
r< s.length?
统计 t中字符被涵盖的情况
结束
已涵盖 t
所有字符?
r++
r-l+1< len?
minl=l
len=r-l+1
统计 l右移对字符涵盖情况的影响
l++

总体流程: 首先需要对字符串 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)

你可能感兴趣的:(算法,算法,leetcode)