双指针与滑动窗口
问题
双指针和滑动窗口有哪些常见题型?
答案
双指针分类
| 类型 | 说明 | 典型题目 |
|---|---|---|
| 对撞指针 | 左右向中间移动 | 两数之和(有序)、接雨水 |
| 快慢指针 | 不同速度移动 | 链表有环、移除元素 |
| 滑动窗口 | 维护一段区间 | 最长无重复子串、最小覆盖子串 |
两数之和(有序数组)
对撞指针
public int[] twoSum(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) return new int[]{left, right};
else if (sum < target) left++;
else right--;
}
return new int[]{};
}
三数之和
排序 + 双指针
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 去重
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.add(List.of(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) left++; // 去重
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) left++;
else right--;
}
}
return result;
}
最长无重复字符子串(滑动窗口)
滑动窗口
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> window = new HashMap<>();
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
window.merge(c, 1, Integer::sum);
// 窗口内有重复字符,收缩左边界
while (window.get(c) > 1) {
char d = s.charAt(left);
window.merge(d, -1, Integer::sum);
left++;
}
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
滑动窗口模板
通用模板
int left = 0;
for (int right = 0; right < n; right++) {
// 1. 扩大窗口:加入 right 元素
window.add(arr[right]);
// 2. 判断是否需要收缩
while (needShrink()) {
// 3. 收缩窗口:移出 left 元素
window.remove(arr[left]);
left++;
}
// 4. 更新结果
result = Math.max(result, right - left + 1);
}
常见面试问题
Q1: 接雨水怎么做?
答案:
双指针法:分别从左右向中间移动,维护左右的最大高度。
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0, water = 0;
while (left < right) {
if (height[left] < height[right]) {
leftMax = Math.max(leftMax, height[left]);
water += leftMax - height[left];
left++;
} else {
rightMax = Math.max(rightMax, height[right]);
water += rightMax - height[right];
right--;
}
}
return water;
}
Q2: 移动零(原地操作)
答案:
快慢指针:slow 指向下一个非零元素应该放的位置。
public void moveZeroes(int[] nums) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != 0) {
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
slow++;
}
}
}