双指针-leetcode算法题总结+练习
1. 常考类型
2. 解题思路(按题型给“可直接执行”的步骤)
2.1 左右夹近(数组已排序或可排序)
二数/三数之和 & 面积类
- 
若题目允许,先排序(可能改变原顺序要先确认题意)。
 - 
设
l=0, r=n-1;根据目标函数选择移动方向:- 
和太小 →
l++;和太大 →r--。 - 
面积/距离最值 → 每步计算答案,同时按规则收缩较差的一侧。
 
 - 
 - 
三数之和:外层枚举
i,内层对l=i+1, r=n-1夹逼,并在相等时跳重。 - 
收集/更新答案,直到
l>=r(或外层循环结束)。 
简模板(Two Sum II / 有序):
int l = 0, r = nums.length - 1;while (l < r) { int sum = nums[l] + nums[r]; if (sum == target) return new int[]{l+1, r+1}; if (sum < target) l++; else r--;}
三数之和(去重!):
Arrays.sort(nums);int n = nums.length;List<List> ans = new ArrayList();for (int i = 0; i  0 && nums[i] == nums[i-1]) continue; int l = i + 1, r = n - 1; while (l < r) { long s = (long)nums[i] + nums[l] + nums[r]; if (s == 0) { ans.add(Arrays.asList(nums[i], nums[l], nums[r])); while (l < r && nums[l] == nums[l+1]) l++; while (l < r && nums[r] == nums[r-1]) r--; l++; r--; } else if (s < 0) l++; else r--; }}
回文串(125):
- 
l=0, r=n-1;跳过非字母数字。 - 
比较同一大小写;相等则双向收缩;不等返回
false。 - 
走完返回
true。 
int l = 0, r = s.length()-1;while (l < r) { while (l < r && !Character.isLetterOrDigit(s.charAt(l))) l++; while (l < r && !Character.isLetterOrDigit(s.charAt(r))) r--; if (Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r))) return false; l++; r--;}return true;
2.2 快慢指针(链表)
判环与入口(141/142)
- 
slow=head, fast=head;fast每次两步、slow一步。 - 
相遇则有环;无相遇到尾则无环。
 - 
找入口:
p1=head, p2=相遇点同速前进,相遇处即入口。 
倒数第 k 个(19 的子步骤)
- 
fast先走k步; - 
fast/slow同步走到fast==null;slow即倒数第 k。 - 
若要删除:用哑节点
dummy,定位到slow的前驱再改指针。 
原地删除看“下一位”(203/237)
- 
用
dummy;cur=dummy; - 
循环条件
cur.next != null; - 
若
cur.next.val==val删之,否则cur=cur.next(删除时不前进)。 
2.3 滑动窗口(子串/子数组)
最长无重复子串(3)
- 
右指针
r扩张,把s[r]放入计数; - 
若违反要求(出现重复),移动左指针
l,边移边减少计数直到合法; - 
每次合法时更新答案
max = max(max, r-l+1)。 
int[] cnt = new int[128];int l = 0, ans = 0;for (int r = 0; r  1) { cnt[s.charAt(l)]--; l++; } ans = Math.max(ans, r - l + 1);}
最小覆盖子串(76)
- 
统计目标串
t的频率need;维护window+have/needMatch。 - 
右扩纳入字符;当
window覆盖need时,尽量左缩并更新最优区间。 - 
返回最短区间。
(实现与你现有熟悉模板一致,这里略。) 
长度最小子数组(209)
- 
sum += nums[r]右扩; - 
当
sum >= target,不断左缩并更新ans; - 
结束返回
ans或 0。 
int l = 0, sum = 0, ans = Integer.MAX_VALUE;for (int r = 0; r = target) { ans = Math.min(ans, r - l + 1); sum -= nums[l++]; }}return ans == Integer.MAX_VALUE ? 0 : ans;
2.4 同向读写指针
去重(26:保留一个)
- 
w=0写指针; - 
枚举
x:若是首元素或x != nums[w-1],写入并w++; - 
返回
w。 
int w = 0;for (int x : nums) if (w == 0 || x != nums[w-1]) nums[w++] = x;return w;
去重(80:最多保留两次)
int w = 0;for (int x : nums) if (w < 2 || x != nums[w-2]) nums[w++] = x;return w;
移除元素(27:不保序最快)
int l = 0, r = n - 1;while (l <= r) { if (nums[l] == val) nums[l] = nums[r--]; else l++;}return r + 1;
2.5 双序列双指针
合并两个有序数组(88,尾填避免搬运)
- 
i=m-1, j=n-1, k=m+n-1; - 
从大到小把较大者放到
k; - 
直到
j<0结束。 
int i=m-1, j=n-1, k=m+n-1;while (j >= 0) { if (i >= 0 && nums1[i] > nums2[j]) nums1[k--] = nums1[i--]; else nums1[k--] = nums2[j--];}
区间交集(986)
- 
两列表各用一指针;
 - 
取交集
[max(a.start,b.start), min(a.end,b.end)]若合法则加入; - 
谁结束早,谁指针前进。
 
3. 易错点清单
- 
边界:
while (l < r)vs<=;三数之和外层/内层去重顺序。 - 
删除时不前进:链表用“看下一位”,否则会漏删连续目标。
 - 
滑窗更新时机:最长类“合法后更新”;最短类“每次缩时更新”。
 - 
排序副作用:是否允许改变原序;涉及索引时要保留原位信息。
 - 
整型溢出:求和/面积用
long。 - 
空/单元素:链表
head==null、数组长度 0/1 要先处理。 
4. leetcode练习题
27 移除元素

示例 1:
输入:nums = [3,2,2,3], val = 3输出:2, nums = [2,2,_,_]解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
class Solution { public int removeElement(int[] nums, int val) { int left =0; for(int i =0; i<nums.length; i++){ if(nums[i] != val){ nums[left] = nums[i]; left++; } } return left; }}


