情景一
示例
从一个经典问题开始:反转数组中的元素。
其思想是将第一个元素与末尾进行交换,再向前移动到下一个元素,并不断地交换,直到它到达中间位置。
可以同时使用两个指针来完成迭代:一个从第一个元素开始,另一个从最后一个元素开始。持续交换它们所指向的元素,直到这两个指针相遇。
示例:
void reverse(int *v, int N) {
int i = 0;
int j = N - 1;
while (i < j) {
swap(v[i], v[j]);
i++;
j--;
}
}
总结
使用双指针技巧的典型场景之一是你想要
从两端向中间迭代数组。
这时你可以使用双指针技巧:
一个指针从始端开始,而另一个指针从末端开始。
值得注意的是,这种技巧经常在 排序 数组中使用。
情景二
示例
从另一个经典问题开始:
给定一个数组和一个值,原地删除该值的所有实例并返回新的长度。
如果我们没有空间复杂度上的限制,那就更容易了。我们可以初始化一个新的数组来存储答案。如果元素不等于给定的目标值,则迭代原始数组并将元素添加到新的数组中。
实际上,它相当于使用了两个指针,一个用于原始数组的迭代,另一个总是指向新数组的最后一个位置。
重新考虑空间限制
现在让我们重新考虑空间受到限制的情况。
我们可以采用类似的策略,我们继续使用两个指针:一个仍然用于迭代,而第二个指针总是指向 下一次添加的位置。
以下代码:
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/