双指针算法详解
双指针算法是一种常用的算法技巧,它通过维护两个指针(索引)在数据结构(通常是数组或链表)中协同工作,以高效解决特定问题。双指针算法通常可以将O(n²)的时间复杂度优化到O(n)。
双指针算法的常见类型
1. 同向双指针(快慢指针)
两个指针从同一侧开始移动,但移动速度不同。
应用场景:
- 链表中的环检测
- 链表中间节点查找
- 原地修改数组(如删除重复元素)
Java示例:删除排序数组中的重复项
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int slow = 0;
for (int fast = 1; fast < nums.length; fast++) {
if (nums[fast] != nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
2. 对向双指针
两个指针分别从数据结构的首尾开始,向中间移动。
应用场景:
- 两数之和
- 反转数组/字符串
- 三数之和
Java示例:反转字符串
public void reverseString(char[] s) {
int left = 0, right = s.length - 1;
while (left < right) {
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
3. 滑动窗口
两个指针维护一个窗口,根据条件动态调整窗口大小。
应用场景:
- 最小覆盖子串
- 长度最小的子数组
- 无重复字符的最长子串
Java示例:长度最小的子数组
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int sum = 0;
int minLen = Integer.MAX_VALUE;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
minLen = Math.min(minLen, right - left + 1);
sum -= nums[left];
left++;
}
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
双指针算法的优势
- 时间复杂度优化:通常能将O(n²)优化到O(n)
- 空间复杂度优化:很多情况下只需要O(1)的额外空间
- 代码简洁:逻辑清晰,实现简单
经典问题示例
两数之和 II - 输入有序数组
public int[] twoSum(int[] numbers, int target) {
int left = 0, right = numbers.length - 1;
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum == target) {
return new int[]{left + 1, right + 1};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return new int[]{-1, -1};
}
盛最多水的容器
public int maxArea(int[] height) {
int left = 0, right = height.length - 1;
int maxArea = 0;
while (left < right) {
int currentArea = Math.min(height[left], height[right]) * (right - left);
maxArea = Math.max(maxArea, currentArea);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
}
使用双指针的注意事项
- 边界条件:始终确保指针不会越界
- 移动条件:明确何时移动哪个指针
- 终止条件:确保循环能够正确终止
- 初始状态:正确初始化指针位置
双指针算法是一种强大而灵活的技巧,掌握它可以帮助你高效解决许多数组和链表相关的问题。