3月11日报(双指针2)
- 彻底弄懂了 双指针 的 mid 题目,过程和理解在下面
- 稍微看了看 JS 为后面做项目打基础
- 继续看了网络
- 晚上光玩了。
盛最多水容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量
理解题意
设两指针 i, j,指向水槽板高度分别为 h[i] , h[j] ,此状态下水槽面积为 S(i,j) 。由于可容纳水的高度由两板中的 短板 决定,因此可得如下 面积公式S(i, j) = min(h[i], h[j]) * (j - i)
无论在什么状态下,决定容器高度的永远是最短的那一边,于是有以下情况: 无论是将短板或是长板向内移动,一定会导致宽度 -1 变短:
- 如果将短板向内移动,水槽的短板
min(h[i], h[j])可能会变大,因此下一个水槽的面积可能变大 - 如果将长板向内移动,水槽的短板
min(h[i], h[j])可能会不变或变小,因此下一个水槽的面积一定减小
因此,将双指针初始化于水槽的左右两端,循环将短板向内移动一格,更新面积最大值,直到两个指针相遇,跳出算法
代码实现
class Solution {
public int maxArea(int[] height) {
int l = 0, r = height.length - 1, ans = 0;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
ans = Math.max(area, ans);
if (height[l] <= height[r]) {
l++;
} else {
r--;
}
}
return ans;
}
}三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。 **注意:**答案中不可以包含重复的三元组
思路解析
首先将数组排序,这样就方便我们去重,还可以根据大小关系移动左右指针。之后进行枚举 nums[i] 将问题变形为 nums[j] + nums[k] = -nums[i]
关于去重
要明确一点是,去重的目的是,不要让同一个数值在同一个位置上出现两次 为什么说 在跳过相同元素时,我们使用的是 if (i > 0 && x == nums[i - 1]) continue; 而不是 nums[n + 1] 第一种去重的是结果集,保证同一个位置不重复尝试相同的数值。第二种 去重的是组合内。会错误地禁止组合内出现重复元素,导致漏解 剩下的看代码
代码实现
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
int n = nums.length;
for (int i = 0; i < n - 2; i++) { // 边界问题,留出两个位置给 j k
int x = nums[i];
// -- 去重 --
if (i > 0 && x == nums[i - 1]) continue; // 去重复值
if (x + nums[i + 1] + nums[i + 2] > 0) break;
// 优化最小值
// 如果 x 加上后面的最小值还是大于 0 ,证明后面的数只会更大 break
if (x + nums[n - 2] + nums[n - 1] < 0) continue;
// 优化最大值
// 如果 x 加上最大的两个值仍小于 0,证明目前这个 x 实在是太小了
// -- 指针部分 --
int j = i + 1;
int k = n - 1;
while (j < k) {
int s = x + nums[j] + nums[k];
if (s > 0) {
k--; // sum 太大 左移动 右指针减小
} else if (s < 0) {
j++; // sum 太小 右移动 左指针增大
} else {
ans.add(List.of(x ,nums[j], nums[k]));
for (j++; j < k && nums[j] == nums[j - 1]; j++);
for (k--; k > j && nums[k] == nums[k + 1]; k--);
// 跳过重复数字
// 1. 先移动指针,对比上一个数是否与下一个数相等
// 2. 如果新位置的数与上一位置相等再移动指针
}
}
}
return ans;
}
}