Skip to content

双指针

指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。如果两个指针方向相反,则称为「对撞指针」。如果两个指针方向相同,则称为「快慢指针」。如果两个指针分别属于不同的数组 / 链表,则称为「分离双指针」。

在数组的区间问题上,暴力算法的时间复杂度往往是 $O(n^2)$。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 $O(n)$。

对撞指针

指的是两个指针 leftright 分别指向序列第一个元素和最后一个元素,然后 left 指针不断递增,right 不断递减,直到两个指针的值相撞(即 left == right),或者满足其他要求的特殊条件为止。

解题

  1. 使用两个指针 left,right。left 指向序列第一个元素,即:left = 0,right 指向序列最后一个元素,即:right = len(nums) - 1。
  2. 在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,left += 1。当满足另外一定条件时,将右指针左移,right -= 1。
  3. 直到两指针相撞(即 left == right),或者满足其他要求的特殊条件时,跳出循环体。

伪代码

left, right = 0, len(nums) - 1

while left < right:
    if 满足要求的特殊条件:
        return 符合条件的值 
    elif 一定条件 1:
        left += 1
    elif 一定条件 2:
        right -= 1

return 没找到 或 找到对应值

适用范围

对撞指针一般用来解决有序数组或者字符串问题:

  • 查找有序数组中满足某些约束条件的一组元素问题:比如二分查找、数字之和等问题。
  • 字符串反转问题:反转字符串、回文数、颠倒二进制等问题。

快慢指针

指的是两个指针从同一侧开始遍历序列,且移动的步长一个快一个慢。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」。两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或者满足其他特殊条件时为止。

解题

  1. 使用两个指针 slow、fast。slow 一般指向序列第一个元素,即:slow = 0,fast 一般指向序列第二个元素,即:fast = 1。
  2. 在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即 slow += 1。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即 fast += 1。
  3. 到快指针移动到数组尾端(即 fast == len(nums) - 1),或者两指针相交,或者满足其他特殊条件时跳出循环体。

伪代码

slow = 0
fast = 1
while 没有遍历完:
    if 满足要求的特殊条件:
        slow += 1
    fast += 1
return 合适的值

适用范围

快慢指针一般用于处理数组中的移动、删除元素问题,或者链表中的判断是否有环、长度问题;

分离双指针

两个指针分别属于不同的数组 / 链表,两个指针分别在两个数组 / 链表中移动。

解题

  1. 使用两个指针 left_1、left_2。left_1 指向第一个数组 / 链表的第一个元素,即:left_1 = 0,left_2 指向第二个数组 / 链表的第一个元素,即:left_2 = 0。
  2. 当满足一定条件时,两个指针同时右移,即 left_1 += 1、left_2 += 1。
  3. 当满足另外一定条件时,将 left_1 指针右移,即 left_1 += 1。
  4. 当满足其他一定条件时,将 left_2 指针右移,即 left_2 += 1。
  5. 当其中一个数组 / 链表遍历完时或者满足其他特殊条件时跳出循环体

伪代码

left_1 = 0
left_2 = 0


while left_1 < len(nums1) and left_2 < len(nums2):
    if 一定条件 1:
        left_1 += 1
        left_2 += 2
    elif 一定条件 2:
        left_1 += 1
    elif 一定条件 3:
        left_2 += 1
return xxx

使用范围

分离双指针一般用于处理有序数组合并,求交集、并集问题。

例题

下面数字是leetcode题号

344

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

直接交换left和right位置的数据就ok;
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        left, right = 0, len(s)-1
        while left <= right:
            s[left], s[right] = s[right], s[left]
            left += 1
            right -= 1

11

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。

设置ans = 0
ans = max(ans,(right-left)*min(arr[left],arr[right]))
class Solution:
    def maxArea(self, height: List[int]) -> int:
        i, j, res = 0, len(height) - 1, 0
        while i < j:
            water = (j - i) * min(height[i], height[j])
            res = max(water, res)
            if height[i] < height[j]:
                i += 1
            else:
                j -= 1
        return res