双指针技巧详解:从基础到进阶(Java 实现)
双指针技巧利用区间单调性或位置关系优化数组算法。文章精选移动零、盛最多水的容器、三数之和及接雨水四道经典题型,分别演示快慢指针与左右指针的应用场景。通过排序、去重及贪心策略,将暴力搜索优化至线性时间复杂度。附带 Java 代码实现与解题思考模版,帮助理解指针初始化、移动条件及收缩逻辑。

双指针技巧利用区间单调性或位置关系优化数组算法。文章精选移动零、盛最多水的容器、三数之和及接雨水四道经典题型,分别演示快慢指针与左右指针的应用场景。通过排序、去重及贪心策略,将暴力搜索优化至线性时间复杂度。附带 Java 代码实现与解题思考模版,帮助理解指针初始化、移动条件及收缩逻辑。


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
在处理数组相关算法时,双指针(Two Pointers)能够巧妙地利用区间单调性或位置关系,将原本需要 O(n^2) 的暴力搜索优化至 O(n)。本文精选四道经典题型,附带代码注释。
class Solution {
public void moveZeroes(int[] nums) {
// slow 指针之前(不含 slow)全是非零数
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
// 当快指针发现非零数时
if (nums[fast] != 0) {
// 如果快慢指针不相等,说明中间有 0,需要交换
if (fast > slow) {
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
}
// 无论是否交换,slow 都要后移,因为当前 slow 位置已被非零数占据
slow++;
}
}
}
}
class Solution {
public int maxArea(int[] height) {
int left = 0, right = height.length - 1;
// 定义左右边界
int max = 0;
// 存储最大面积
while (left < right) {
// 1. 计算当前面积:宽 (right - left) * 高 (左右两端的最小值)
int currentArea = Math.min(height[left], height[right]) * (right - left);
// 2. 更新全局最大面积
max = Math.max(max, currentArea);
// 3. 贪心策略:哪边矮,就移动哪边的指针
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return max;
}
}
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums);
// 1. 先排序
int n = nums.length;
for (int i = 0; i < n; i++) {
// 如果当前数大于 0,由于数组有序,后续三个数之和一定大于 0
if (nums[i] > 0) break;
// 2. 对第一个数 a 去重:如果当前数和前一个数相同,跳过
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1, right = n - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 3. 对第二个数 b 去重
while (left < right && nums[left] == nums[left + 1]) left++;
// 4. 对第三个数 c 去重
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} {
right--;
}
}
}
ans;
}
}
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
// 记录左边和右边遇到的最高高度
int res = 0;
while (left < right) {
// 更新左右两侧目前的最高墙
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
// 如果左边的墙比右边的墙矮
// 意味着:对于 left 这个点,接多少水取决于左侧的 leftMax(因为右侧一定有比它更高的墙)
if (leftMax < rightMax) {
res += (leftMax - height[left]);
left++;
} else {
// 反之,right 这个点接多少水取决于右侧的 rightMax
res += (rightMax - height[right]);
right--;
}
}
return res;
}
}
场景识别:
核心三要素:
通过以上四道题的练习,你应该能感受到双指针在降低时间复杂度方面的巨大威力。