【算法题解答·二】双指针法
接上文 【算法方法总结·二】双指针的一些技巧和注意事项
双指针法相关题目如下:
27.移除元素 简单
- 此题 两种写法均可
- 但要求中有原地移除,根据上文技巧的内容,选 快慢指针法更优
class Solution {
public int removeElement(int[] nums, int val) {
int len = nums.length;
// slow,fast 均初始化为 0
int slow = 0;
for (int fast = 0; fast < len; fast++) {
// fast来寻找新数组,slow用来更新数组
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow; // 返回新数组的个数
}
}
977.有序数组的平方 简单
- 数组有序,根据上文技巧的内容,选 相向双指针法
- 数组 平方最大值 在 数组的两端,所以指针从两端往中间走
class Solution {
public int[] sortedSquares(int[] nums) {
// 双向双指针法
int left = 0, right = nums.length - 1;
int[] result = new int[nums.length];
int index = nums.length - 1; // 最大的存在 result 数组最后
while (left <= right) {
if (nums[left] * nums[left] > nums[right] * nums[right]) {
result[index--] = nums[left] * nums[left];
++left;
} else { // nums[left] * nums[left] <= nums[right] * nums[right]
result[index--] = nums[right] * nums[right];
--right;
}
}
return result;
}
}
283.移动零 简单
- 要求中有原地操作,根据上文技巧的内容,选 快慢指针法更优
- 此题与上述 27.移除元素 解法几乎一样
// 用 slow,fast 双指针,快指针 fast 遍历到不为 0 的元素往前移到慢指针 slow 的位置上
class Solution {
public void moveZeroes(int[] nums) {
int len = nums.length;
int slow = 0;
for (int fast = 0; fast < len; fast++) {
if (nums[fast] != 0) {
nums[slow] = nums[fast];
slow++;
}
}
for (int i = slow; i < len; i++) {
nums[i] = 0;
}
}
}
11.盛最多水的容器
- 两端协调问题,根据上文技巧的内容,选 相向双指针法
- 木桶原理,装水容量受最短木板影响
class Solution {
public int maxArea(int[] height) {
// 双向双指针法
int left = 0, right = height.length - 1;
int ans = 0; // 容量(面积)
// 为啥不是 left <= right,因为 left == right 时容量为0,没意义
while (left < right) {
// 木桶原理,装水容量受最短木板影响,所以取 min
int area = (right - left) * Math.min(height[left], height[right]);
ans = Math.max(ans, area);
// 受最短木板影响,尽量留下长的那块木板,所以 left 不动,right--
if (height[left] > height[right]) {
right--;
} else { // 尽量留下长的那块木板,所以 right 不动,left++
left++;
}
}
return ans;
}
}
15.三数之和
- 三个数 分别设为
i,j,k
,首先要保证有序,就能用相向双指针法来降低时间复杂度了 - 本题还需要加一个去重操作
// 先遍历第一个元素 i,然后让 j = i+1,k = 最后一个元素,j,k为相向双指针
// 记得去重
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums); // 保证有序
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) return ans;
// 去重 i
if (i > 0 && nums[i] == nums[i - 1]) continue;
int j = i + 1;
int k = nums.length - 1;
// j,k 相向双指针
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
} else { // sum==0
ans.add(Arrays.asList(nums[i], nums[j], nums[k]));
// 去重 j,k
while (j < k && nums[j] == nums[j + 1]) j++;
while (j < k && nums[k] == nums[k - 1]) k--;
j++;
k--;
}
}
}
return ans;
}
}
42.接雨水 困难
- 听说面试经常考,得背熟
- 此题可以用单调栈的方法做,但这里只考虑相向双指针的方法
- 相向双指针法是对暴力法的优化,暴力法按下图所示一列一列计算,会超时
- 每到一个柱子都向两侧遍历一遍,这其实是有重复计算的,把每一个位置的左边最高高度记录在一个数组上(
maxLeft
),右边最高高度记录在一个数组上(maxRight
),这样就避免了重复计算
// 按列计算值
class Solution {
public int trap(int[] height) {
int len = height.length;
if (len <= 2) {
return 0;
}
// 相向双指针法,maxLeft,maxRight 跟着 l,r 的变化而变化
int maxLeft = height[0], maxRight = height[len - 1];
int l = 1, r = len - 2; // 两端下标 0 和 len-1 处无法蓄水
int res = 0;
while (l <= r) {
// 不管移动哪边,都更新
maxLeft = Math.max(maxLeft, height[l]);
maxRight = Math.max(maxRight, height[r]);
// 较小的一边先确定量值
if (maxLeft < maxRight) {
res += maxLeft - height[l];
l++;
} else {
res += maxRight - height[r];
r--;
}
}
return res;
}
}
算法题解答系列
【算法题解答·一】二分法
【算法题解答·二】双指针法
【算法题解答·三】滑动窗口
【算法题解答·四】字符串操作
【算法题解答·五】链表操作
【算法题解答·六】栈队列堆