情景一

示例

从一个经典问题开始:反转数组中的元素。
其思想是将第一个元素与末尾进行交换,再向前移动到下一个元素,并不断地交换,直到它到达中间位置。

可以同时使用两个指针来完成迭代:一个从第一个元素开始,另一个从最后一个元素开始。持续交换它们所指向的元素,直到这两个指针相遇

示例:

  1. void reverse(int *v, int N) {
  2. int i = 0;
  3. int j = N - 1;
  4. while (i < j) {
  5. swap(v[i], v[j]);
  6. i++;
  7. j--;
  8. }
  9. }

总结

使用双指针技巧的典型场景之一是你想要
从两端向中间迭代数组。

这时你可以使用双指针技巧:
一个指针从始端开始,而另一个指针从末端开始。
值得注意的是,这种技巧经常在 排序 数组中使用。

情景二

两个不同步的指针

示例

从另一个经典问题开始:
给定一个数组和一个值,原地删除该值的所有实例并返回新的长度
如果我们没有空间复杂度上的限制,那就更容易了。我们可以初始化一个新的数组来存储答案。如果元素不等于给定的目标值,则迭代原始数组并将元素添加到新的数组中。

实际上,它相当于使用了两个指针,一个用于原始数组的迭代,另一个总是指向新数组的最后一个位置。

重新考虑空间限制

现在让我们重新考虑空间受到限制的情况。
我们可以采用类似的策略,我们继续使用两个指针:一个仍然用于迭代,而第二个指针总是指向 下一次添加的位置
以下代码:

int removeElement(vector<int>& nums, int val) {
    int k = 0;
    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] != val) {
            nums[k] = nums[i];
            ++k;
        }
    }
    return k;
}

在上面的例子中,我们使用两个指针,一个快指针 i 和一个慢指针 k 。i 每次移动一步,而 k 只在添加新的被需要的值时才移动一步。

总结

这是使用双指针技巧的一种非常常见的情况:
同时有一个慢指针和一个快指针。
解决这类问题的关键是
确定两个指针的移动策略。
与前一个场景类似,有时可能需要在使用双指针技巧之前对数组进行排序,也可能需要运用贪心想法来决定你的运动策略。

原文

https://leetcode-cn.com/explore/featured/card/array-and-string/201/two-pointer-technique/782/
https://leetcode-cn.com/explore/featured/card/array-and-string/201/two-pointer-technique/786/