题目:移动零
原题链接:移动零
题解
思路:快慢指针(双指针)法
- 慢指针(l):用于标记当前非零元素应该存放的位置。
- 快指针(h):用于遍历整个数组。
public class Test {
public static void moveZeroes(int[] nums) {
int l = 0, h = 0;
for (; h < nums.length; h++) {
if (nums[h] != 0) {
nums[l++] = nums[h];
}
}
for (; l < nums.length; l++) {
nums[l] = 0;
}
}
public static void main(String[] args) {
int[] nums = {0, 1, 0, 3, 12};
moveZeroes(nums);
System.out.println(Arrays.toString(nums));
}
}
题目:盛最多水的容器
原题链接:盛最多水的容器
题解
思路:双指针
public class Test {
public static int maxArea(int[] height) {
int l = 0;
int h = height.length - 1;
int area = 0;
while (l < h) {
area = Math.max(Math.min(height[l], height[h]) * (h - l), area);//(h - l)矩形宽度
if (height[l] < height[h]) {
l++;
} else {
h--;
}
}
return area;
}
public static void main(String[] args) {
int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7};
System.out.println(maxArea(height));
}
}
- 如果移动较小高度的指针,有可能找到一个更高的柱子,从而增加可能的最大面积。
- 反之,如果移动较高高度的指针,由于
高度已经固定为较小值
,移动较高指针只会减小宽度,不会增加高度,因此可能会导致面积减小或保持不变。
题目:三数之和
原题链接:三数之和
题解
思路:一层枚举+双指针
public class Test {
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length < 3) return res;
Arrays.sort(nums);
int target = 0;
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
//因为数组排序了 如果num[i]大于0 则后面不可能选出三个数和为0
if (nums[i] > 0) break;
target = 0 - nums[i];
int l = i + 1, h = nums.length - 1;
while (l < h) {
if (nums[l] + nums[h] > target) {
h--;
} else if (nums[l] + nums[h] < target) {
l++;
} else if (nums[l] + nums[h] == target) {
// 跳过重复元素
while (l < h && nums[l] == nums[l + 1]) l++;
while (l < h && nums[h] == nums[h - 1]) h--;
res.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[h])));
h--;
l++;
}
}
}
return res;
}
public static void main(String[] args) {
int[] nums = {-1, 0, 1, 2, -1, -4};
System.out.println(threeSum(nums));
}
}
while里面的去重, 可以用下面两种方法。(为什么去重?看一个数组[-2, 0, 0, 2, 2]
)
// 跳过重复元素
while (l < h && nums[l] == nums[l + 1]) l++;
while (l < h && nums[h] == nums[h - 1]) h--;
if (nums[l] == nums[l - 1] && ((h + 1) < nums.length && nums[h] == nums[h + 1])) {
l++;
h--;
continue;
}
题目:接雨水
原题链接:接雨水
题解
思路:双指针
核心思想:对于任意位置 i
,能够存储的雨水量取决于位置 i
左侧和右侧的最大高度中的较小值减去 height[i]
。即 min(leftMax, rightMax) - height[i]
。我感觉这个很好理解,理解了这个,这道题就简单了
public class T42 {
public static int trap(int[] height) {
if (height == null || height.length <= 2) {
return 0;
}
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
int res = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
res += leftMax - height[left];
}
left++;
} else {
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
res += rightMax - height[right];
}
right--;
}
}
return res;
}
public static void main(String[] args) {
int[] height = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
System.out.println(trap(height));
}
}
解释代码
left
和right
指针分别初始化为数组的最左和最右端。leftMax
和rightMax
分别记录遍历过程中左侧和右侧的最大高度。res
用于累计存储的雨水量。
比较左右高度
if (height[left] < height[right]) {
- 如果左侧高度小于右侧高度,则处理左侧:
- 更新左侧最大高度:
if (height[left] >= leftMax) { leftMax = height[left]; }
- 如果当前左侧高度大于等于
leftMax
,更新leftMax
。
- 如果当前左侧高度大于等于
- 计算左侧可以存储的雨水:
else { res += leftMax - height[left]; }
- 否则,当前柱子可以存储的雨水量为
leftMax - height[left]
。将其累加到res
。
- 否则,当前柱子可以存储的雨水量为
- 移动左指针:
left++;
- 更新左侧最大高度:
处理右侧
else {
- 如果右侧高度小于等于左侧高度,则处理右侧:
- 更新右侧最大高度:
if (height[right] >= rightMax) { rightMax = height[right]; }
- 如果当前右侧高度大于等于
rightMax
,更新rightMax
。
- 如果当前右侧高度大于等于
- 计算右侧可以存储的雨水:
else { res += rightMax - height[right]; }
- 否则,当前柱子可以存储的雨水量为
rightMax - height[right]
。将其累加到res
。
- 否则,当前柱子可以存储的雨水量为
- 移动右指针:
right--;
- 更新右侧最大高度:
为什么双指针法有效
- 关键思想:
- 对于任意位置
i
,能够存储的雨水量取决于位置i
左侧和右侧的最大高度中的较小值减去height[i]
。即min(leftMax, rightMax) - height[i]
。
- 对于任意位置
- 左右指针:
- 当
height[left] < height[right]
时,右侧的高度至少是height[right]
,这意味着左侧的最大高度决定了当前柱子left
的存储量。 - 同理,当
height[left] >= height[right]
时,左侧的高度至少是height[left]
,这意味着右侧的最大高度决定了当前柱子right
的存储量。
- 当
力扣有一位大佬给出5种方法,确实让人佩服。5种解答
❤觉得有用的可以留个关注~~❤