跳到主要内容

双指针

双指针通常从两端向中间靠拢,或者快慢指针同向移动,在有序数组或特定约束下能显著降低复杂度。

125. 验证回文串 (Valid Palindrome)

题目描述

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false

解题思路

使用双指针 ij 分别指向字符串的头和尾。

  1. 跳过非字母数字字符。
  2. 比较 Character.toLowerCase(s.charAt(i))Character.toLowerCase(s.charAt(j))
  3. 如果不相等则不是回文;否则 i++, j-- 继续。

Java 代码实现

class Solution {
public boolean isPalindrome(String s) {
int i = 0, j = s.length() - 1;
while (i < j) {
while (i < j && !Character.isLetterOrDigit(s.charAt(i))) i++;
while (i < j && !Character.isLetterOrDigit(s.charAt(j))) j--;
if (Character.toLowerCase(s.charAt(i++)) != Character.toLowerCase(s.charAt(j--))) {
return false;
}
}
return true;
}
}

392. 判断子序列 (Is Subsequence)

题目描述

给定字符串 st ,判断 s 是否为 t子序列

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde" 的一个子序列,而 "aec" 不是)。

解题思路

双指针遍历。i 指向 sj 指向 t

  1. 如果 s.charAt(i) == t.charAt(j),则 i++
  2. 无论是否匹配,j++ 始终执行。
  3. 最后判断 i 是否等于 s.length()

Java 代码实现

class Solution {
public boolean isSubsequence(String s, String t) {
int i = 0, j = 0;
while (i < s.length() && j < t.length()) {
if (s.charAt(i) == t.charAt(j)) i++;
j++;
}
return i == s.length();
}
}

167. 两数之和 II - 输入有序数组 (Two Sum II - Input Array Is Sorted)

题目描述

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序 排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。

如果设这两个数分别是 numbers[index1]numbers[index2] ,则 1 <= index1 < index2 <= numbers.length

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用 O(1) 的额外空间。

解题思路

头尾指针

  1. i = 0, j = numbers.length - 1
  2. 如果 sum == target 返回。
  3. 如果 sum < target,说明需要变大,i++
  4. 如果 sum > target,说明需要变小,j--

Java 代码实现

class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0, j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum == target) return new int[]{i + 1, j + 1};
if (sum < target) i++;
else j--;
}
return new int[]{-1, -1};
}
}

11. 盛最多水的容器 (Container With Most Water)

题目描述

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明: 你不能倾斜容器。

解题思路

贪心 + 双指针

  1. 设置 ij 指向两端。
  2. 容器高度由较低的一边决定:h = min(height[i], height[j])
  3. 计算面积并更新最大值。
  4. 移动较小高度的一边,因为只有移动短板才可能让高度增加,进而弥补底变短带来的损失。

Java 代码实现

class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1;
int max = 0;
while (i < j) {
max = Math.max(max, Math.min(height[i], height[j]) * (j - i));
if (height[i] < height[j]) i++;
else j--;
}
return max;
}
}

15. 三数之和 (3Sum)

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0

请你返回所有和为 0 且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

解题思路

排序 + 双指针

  1. 排序数组。
  2. 遍历第一个数 nums[i]。如果 nums[i] > 0 后面不可能为 0,跳出。
  3. 判断去重:如果 i > 0 && nums[i] == nums[i-1] 跳过。
  4. 使用 left = i + 1, right = n - 1 寻找满足条件的 sum = 0
  5. 找到解后,leftright 也要分别跳过重复项。

Java 代码实现

class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int l = i + 1, r = nums.length - 1;
while (l < r) {
int sum = nums[i] + nums[l] + nums[r];
if (sum == 0) {
res.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 (sum < 0) l++;
else r--;
}
}
return res;
}
}