双指针
双指针通常从两端向中间靠拢,或者快慢指针同向移动,在有序数组或特定约束下能显著降低复杂度。
125. 验证回文串 (Valid Palindrome)
题目描述
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
解题思路
使用双指针 i 和 j 分别指向字符串的头和尾。
- 跳过非字母数字字符。
- 比较
Character.toLowerCase(s.charAt(i))和Character.toLowerCase(s.charAt(j))。 - 如果不相等则不是回文;否则
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)
题目描述
给定字符串 s 和 t ,判断 s 是否为 t 的 子序列 。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace" 是 "abcde" 的一个子序列,而 "aec" 不是)。
解题思路
双指针遍历。i 指向 s,j 指向 t。
- 如果
s.charAt(i) == t.charAt(j),则i++。 - 无论是否匹配,
j++始终执行。 - 最后判断
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] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用 O(1) 的额外空间。
解题思路
头尾指针。
i = 0, j = numbers.length - 1。- 如果
sum == target返回。 - 如果
sum < target,说明需要变大,i++。 - 如果
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 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
解题思路
贪心 + 双指针。
- 设置
i和j指向两端。 - 容器高度由较低的一边决定:
h = min(height[i], height[j])。 - 计算面积并更新最大值。
- 移动较小高度的一边,因为只有移动短板才可能让高度增加,进而弥补底变短带来的损失。
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 != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。
请你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
解题思路
排序 + 双指针。
- 排序数组。
- 遍历第一个数
nums[i]。如果nums[i] > 0后面不可能为 0,跳出。 - 判断去重:如果
i > 0 && nums[i] == nums[i-1]跳过。 - 使用
left = i + 1, right = n - 1寻找满足条件的sum = 0。 - 找到解后,
left和right也要分别跳过重复项。
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;
}
}