1. 两数之和

题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

1
2
3
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

1
2
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

1
2
输入:nums = [3,3], target = 6
输出:[0,1]

题解

1. 暴力

两次枚举,需要两次遍历,时间复杂度O(n²)

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < n; i++) { // 枚举 i
for (int j = i + 1; j < nums.length; j++) { // 枚举 i 右边的 j
if (nums[i] + nums[j] == target) { // 满足要求
return new int[]{i, j}; // 返回两个数的下标
}
}
}
// 题目保证有解,循环中一定会 return
// 所以这里无需 return,毕竟代码不会执行到这里
}
}

ACM模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

int n = scanner.nextInt();
int target = scanner.nextInt();

int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt();
}

// 两数之和
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (nums[i] + nums[j] == target) {
System.out.println(i + " " + j);
return;
}
}
}
}
}

2. 双指针

由于题目所给数组已经有序排列,则可以从两端开始取数,如果两端取数的和大于目标,则说明:和为目标值的两个元素一定在两端范围内,时间复杂度O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int[] twoSum(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (true) {
if ((nums[right] + nums[left]) > target){
right--;
}
else if((nums[right] + nums[left]) < target){
left++;
}
else{
return new int[]{left, right};
}
}
}
}

3. HashMap

使用HashMap的key作为元素值,value作为数组下标

把元素存到HashMap中,去查找HashMap中与元素相加等于target的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.HashMap;

class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
int need = target - nums[i];
if(map.containsKey(need)){
return new int[]{map.get(need), i};
}
map.put(nums[i], i);
}
return new int[]{-1,-1};
}
}

心得

很多涉及到「两个变量」的题目,都可以枚举其中一个变量,把它当成常量看待,从而转化成「一个变量」的问题。

代码实现时,通常来说「枚举右,寻找左」是更加好写的。


2. 统计和小于目标的下标对数目

题目描述

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target ,请你返回满足 0 <= i < j < nnums[i] + nums[j] < target 的下标对 (i, j) 的数目。

示例 1:

1
2
3
4
5
6
7
输入:nums = [-1,1,2,3,1], target = 2
输出:3
解释:总共有 3 个下标对满足题目描述:
- (0, 1) ,0 < 1 且 nums[0] + nums[1] = 0 < target
- (0, 2) ,0 < 2 且 nums[0] + nums[2] = 1 < target
- (0, 4) ,0 < 4 且 nums[0] + nums[4] = 0 < target
注意 (0, 3) 不计入答案因为 nums[0] + nums[3] 不是严格小于 target 。

示例 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:nums = [-6,2,5,-2,-7,-1,3], target = -2
输出:10
解释:总共有 10 个下标对满足题目描述:
- (0, 1) ,0 < 1 且 nums[0] + nums[1] = -4 < target
- (0, 3) ,0 < 3 且 nums[0] + nums[3] = -8 < target
- (0, 4) ,0 < 4 且 nums[0] + nums[4] = -13 < target
- (0, 5) ,0 < 5 且 nums[0] + nums[5] = -7 < target
- (0, 6) ,0 < 6 且 nums[0] + nums[6] = -3 < target
- (1, 4) ,1 < 4 且 nums[1] + nums[4] = -5 < target
- (3, 4) ,3 < 4 且 nums[3] + nums[4] = -9 < target
- (3, 5) ,3 < 5 且 nums[3] + nums[5] = -3 < target
- (4, 5) ,4 < 5 且 nums[4] + nums[5] = -8 < target
- (4, 6) ,4 < 6 且 nums[4] + nums[6] = -4 < target

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int countPairs(List<Integer> nums, int target) {
Collections.sort(nums);
int n = nums.size();
int num = 0;
for (int i = 0; i < n; i++) {
int x = nums.get(i);
int j = n - 1;
while(j > i) {
if (nums.get(j) + x < target){
j--;
num++;
}
else break;
}
}
return num;
}
}

要点

List不能通过[]下标访问元素,通过list.get(i)获取元素,list.set(i, x) 设置元素,list.size()获取长度


3. 三数之和

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:

1
2
3
4
5
6
7
8
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

1
2
3
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

1
2
3
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

题解

  1. 输出的顺序和三元组的顺序并不重要。则可以规定 i < j < k
  2. 答案中不可以包含重复的三元组。只需要跳过重复的枚举数(确保三个数字中必有一个不同)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // Java数组排序
int n = nums.length;
List<List<Integer>> list = new ArrayList();
for(int i = 0; i < nums.length-2; i++){
if (i > 0 && nums[i] == nums[i-1]) {
continue;
}
// 优化1:如果num[i] 和他之后最小的两个数相加大于零,则他和之后任意两数相加都大于0
if ((nums[i] + nums[i+1] + nums[i+2]) > 0) {
break;
}
// 优化2:如果nums[i] 和他之后最大的两个数相加小于零,则他和之后任意两数相加都小于0,可以直接跳过这个i
if ((nums[i] + nums[n-1] + nums[n-2]) < 0) {
continue;
}

int j = i + 1;
int k = n - 1;
while(j < k) {
int s = nums[i] + nums[j] + nums[k];
if (s > 0) {
k--;
}
else if (s < 0) {
j++;
}
else {
list.add(Arrays.asList(nums[i], nums[j], nums[k]));
j++;
while(j < k && nums[j] == nums[j-1]) {
j++;
}
k--;
while(k > j && nums[k] == nums[k+1]) {
k--;
}
}
}
}
return list;
}
}

思路

  • 首先对数组进行排序,排序后固定一个数 nums[i],再使用左右指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集
  • 如果 nums[i]大于 0,则三数之和必然无法等于 0,结束循环
  • 如果 nums[i] == nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
  • 当 sum == 0 时,nums[L] == nums[L+1] 则会导致结果重复,应该跳过,L++
  • 当 sum == 0 时,nums[R] == nums[R−1] 则会导致结果重复,应该跳过,R−−
    时间复杂度:O(n ²),n 为数组长度

4. 最接近的三数之和

题目描述

给定一个长度为 n 的整数数组 nums 和 一个整数 target

请你从 nums 中选出三个在 不同下标位置 的整数,使它们的和与 target 最接近

返回这三个数的和。

假定每组输入只存在 恰好 一个解。

示例 1:

1
2
3
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2)。

示例 2:

1
2
3
输入:nums = [0,0,0], target = 1
输出:0
解释:与 target 最接近的和是 0(0 + 0 + 0 = 0)。

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length;
int ans = Integer.MAX_VALUE / 2;
for (int i = 0; i < n-2; i++) {
int x = nums[i];
// 去重
if (i > 0 && x == nums[i - 1]) {
continue;
}
// 如果最小的三数都大于target,则不用往后枚举,最小的三数即为最接近
int s = x + nums[i + 1] + nums[i + 2];
if (s > target) {
if (s - target < Math.abs(ans - target)) {
ans = s;
}
break;
}
// 如果最大的三数都小于target,则不用往前枚举,最大的三数即为最接近
s = x + nums[n - 2] + nums[n - 1];
if (s < target) {
if (target - s < Math.abs(ans - target)) {
ans = s;
}
continue;
}
// 双指针:
int j = i + 1;
int k = n - 1;
while (j < k) {
s = x + nums[j] + nums[k];
if (s == target) { // 三数之和等于target直接返回
return target;
}
if (Math.abs(s - target) < Math.abs(ans - target)) { // 新的三数之和与target更接近,更新三数之和
ans = s;
}
if (s > target) { // 三数之和大于target,减小
k --;
}
else { // 三数之和小于target,增大
j++;
}
}
}
return ans;
}
}

5. 四数之和

题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

1
2
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

1
2
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
int n = nums.length;
for (int a = 0; a < n - 3; a++) {
long x = nums[a];
if (a > 0 && x == nums[a - 1]) continue; // 去重
if (x + nums[a + 1] + nums[a + 2] + nums[a + 3] > target) break;
if (x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) continue;
for (int b = a + 1; b < n - 2; b++) {
long y = nums[b];
if (b > a + 1 && y == nums[b - 1]) continue; // 跳过重复数字
if (x + y + nums[b + 1] + nums[b + 2] > target) break; // 优化一
if (x + y + nums[n - 2] + nums[n - 1] < target) continue; // 优化二
int c = b + 1;
int d = n - 1;
while (c < d) {
long s = x + y + nums[c] + nums[d];
if (s > target) d--;
else if (s < target) c++;
else {
ans.add(List.of((int)x, (int)y, nums[c], nums[d]));
for (c++; c < d && nums[c] == nums[c - 1]; c++);
for (d--; d > c && nums[d] == nums[d + 1]; d--);
}
}
}
}
return ans;
}
}

6. 有效三角形的个数

题目描述

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

1
2
3
4
5
6
输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

示例 2:

1
2
输入: nums = [4,2,3,4]
输出: 4

错解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public int triangleNumber(int[] nums) {
// 1. 对nums进行排序
Arrays.sort(nums);
// 2. 固定遍历
int n = nums.length;
int ans = 0;
for (int a = 0; a < n-2; a++) {
int b = a + 1;
int c = n - 1;
while(b < c) {
if (nums[c] - nums[a] < nums[b]) {
// 三角形成立
ans += c - b;
b++;
}
else {
// 三角形不成立
c--;
}
}
}
return ans;
}
}

错解原因:nums[c] - nums[a] < nums[b] 则说明两小边必大于大边

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
int ans = 0;
// 固定大边
for (int c = n - 1; c > 1; c--) {
int a = 0;
int b = c - 1;
while (b > a) {
if (nums[a] + nums[b] > nums[c]) {
ans += b - a;
b--;
}
else {
a++;
}
}
}
return ans;
}
}

优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int ans = 0;
for (int k = nums.length - 1; k > 1; k--) {
int c = nums[k];
if (nums[0] + nums[1] > c) { // 优化一
ans += (k + 1) * k * (k - 1) / 6;
break;
}
if (nums[k - 2] + nums[k - 1] <= c) { // 优化二
continue;
}
int i = 0; // a=nums[i]
int j = k - 1; // b=nums[j]
while (i < j) {
if (nums[i] + nums[j] > c) {
ans += j - i;
j--;
} else {
i++;
}
}
}
return ans;
}
}

7. 盛水最多的容器

题目描述

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

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

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

**说明:**你不能倾斜容器。

示例 1:

img

1
2
3
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

1
2
输入:height = [1,1]
输出:1

题解

错误解法:暴力 时间复杂度O(n²)

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public int maxArea(int[] height) {
int n = height.length;
int ans = 0;
for (int l = 0; l < n; l++) {
for (int r = n - 1; r > l; r--){
ans = Math.max(ans, (r - l) * Math.min(height[l], height[r]));
}
}
return ans;
}
}

正确解法:双指针 移动height较小的边 (因为若想扩大面积,移动双指针长度必定减小,则必须使高度增加,则优先移动小边) 时间复杂度O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int maxArea(int[] height) {
int n = height.length;
int left = 0;
int right = n - 1;
int ans = 0;

while (left < right) {
int h = Math.min(height[left], height[right]);
int area = h * (right - left);
ans = Math.max(ans, area);

if (height[left] < height[right]) {
left++;
}
else {
right--;
}
}
return ans;
}
}

优化思路:移动指针后可以进行一个高度判断:如果高度比原位置低,则不用计算面积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length - 1;
int ans = 0;

while (left < right) {
int leftHeight = height[left];
int rightHeight = height[right];

int area = (right - left) * Math.min(leftHeight, rightHeight);
ans = Math.max(ans, area);

if (leftHeight < rightHeight) {
while (left < right && height[left] <= leftHeight) {
left++;
}
} else {
while (left < right && height[right] <= rightHeight) {
right--;
}
}
}

return ans;
}
}

8. 接雨水

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

img

1
2
3
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

1
2
3
输入:height = [4,2,0,3,2,5]
输出:9

题解

image-20260919164923349

计算每一块能接的雨水量:左右两边记录高度的最小值 - 当前高度

记录:高度更新的最大值

如果当前高度大于记录高度的最小值,则更新记录高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int trap(int[] height) {
int n = height.length;
int ans = 0;
int left = 0;
int right = n - 1;
int pre_max = 0;
int suf_max = 0;
while (left <= right) {
pre_max = Math.max(pre_max, height[left]);
suf_max = Math.max(suf_max, height[right]);
if (pre_max < suf_max) {
ans += pre_max - height[left];
left++;
}
else {
ans += suf_max - height[right];
right--;
}
}
return ans;
}
}

9. 验证回文串

题目描述

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

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

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

示例 1:

1
2
3
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

示例 2:

1
2
3
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。

示例 3:

1
2
3
4
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

题解

时间复杂度 O(n),空间复杂度 O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public boolean isPalindrome(String s) {
s = s.toLowerCase().replaceAll("[^a-z0-9]", "");
String[] arr = s.split("");
int left = 0;
int right = arr.length - 1;
while (left <= right) {
if (arr[left].equals(arr[right])) {
left ++;
right --;
}
else {
return false;
}
}
return true;
}
}

优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isPalindrome(String s) {
int i = 0;
int j = s.length() - 1;
while (i < j) {
// 去除掉不为数字或字母的字符:直接跳过
if (!Character.isLetterOrDigit(s.charAt(i))) {
i++;
} else if (!Character.isLetterOrDigit(s.charAt(j))) {
j--;
} else if (Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(s.charAt(j))) {
i++;
j--;
} else {
return false;
}
}
return true;
}
}

要点

Character.isLetterOrDigit() 判断某个字符是不是”数字或字母”

Character.toLowerCase() 将字符转为小写

== 比较的是地址,比较值要用.euqals()


10. 给植物浇水II

题目描述

Alice 和 Bob 打算给花园里的 n 株植物浇水。植物排成一行,从左到右进行标记,编号从 0n - 1 。其中,第 i 株植物的位置是 x = i

每一株植物都需要浇特定量的水。Alice 和 Bob 每人有一个水罐,最初是满的 。他们按下面描述的方式完成浇水:

  • Alice 按 从左到右 的顺序给植物浇水,从植物 0 开始。Bob 按 从右到左 的顺序给植物浇水,从植物 n - 1 开始。他们 同时 给植物浇水。
  • 无论需要多少水,为每株植物浇水所需的时间都是相同的。
  • 如果 Alice/Bob 水罐中的水足以 完全 灌溉植物,他们 必须 给植物浇水。否则,他们 首先(立即)重新装满罐子,然后给植物浇水。
  • 如果 Alice 和 Bob 到达同一株植物,那么当前水罐中水 更多 的人会给这株植物浇水。如果他俩水量相同,那么 Alice 会给这株植物浇水。

给你一个下标从 0 开始的整数数组 plants ,数组由 n 个整数组成。其中,plants[i] 为第 i 株植物需要的水量。另有两个整数 capacityAcapacityB 分别表示 Alice 和 Bob 水罐的容量。返回两人浇灌所有植物过程中重新灌满水罐的 次数

示例 1:

1
2
3
4
5
6
7
8
输入:plants = [2,2,3,3], capacityA = 5, capacityB = 5
输出:1
解释:
- 最初,Alice 和 Bob 的水罐中各有 5 单元水。
- Alice 给植物 0 浇水,Bob 给植物 3 浇水。
- Alice 和 Bob 现在分别剩下 3 单元和 2 单元水。
- Alice 有足够的水给植物 1 ,所以她直接浇水。Bob 的水不够给植物 2 ,所以他先重新装满水,再浇水。
所以,两人浇灌所有植物过程中重新灌满水罐的次数 = 0 + 0 + 1 + 0 = 1 。

示例 2:

1
2
3
4
5
6
7
8
输入:plants = [2,2,3,3], capacityA = 3, capacityB = 4
输出:2
解释:
- 最初,Alice 的水罐中有 3 单元水,Bob 的水罐中有 4 单元水。
- Alice 给植物 0 浇水,Bob 给植物 3 浇水。
- Alice 和 Bob 现在都只有 1 单元水,并分别需要给植物 1 和植物 2 浇水。
- 由于他们的水量均不足以浇水,所以他们重新灌满水罐再进行浇水。
所以,两人浇灌所有植物过程中重新灌满水罐的次数 = 0 + 1 + 1 + 0 = 2 。

示例 3:

1
2
3
4
5
6
输入:plants = [5], capacityA = 10, capacityB = 8
输出:0
解释:
- 只有一株植物
- Alice 的水罐有 10 单元水,Bob 的水罐有 8 单元水。因此 Alice 的水罐中水更多,她会给这株植物浇水。
所以,两人浇灌所有植物过程中重新灌满水罐的次数 = 0 。

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public int minimumRefill(int[] plants, int capacityA, int capacityB) {
int n = plants.length;
int count = 0;
int left = 0;
int right = n - 1;
int A = capacityA;
int B = capacityB;
while (left < right) {

if (B >= plants[right] && A >= plants[left]) {
B -= plants[right];
A -= plants[left];
left++;
right--;
}
else if (A < plants[left]) {
A = capacityA;
count ++;
}else if (B < plants[right]) {
B = capacityB;
count++;
}
}
if (left == right) {
if (Math.max(A, B) < plants[left]) {
count++;
}
}

return count;
}
}

优化

while 一次操作即为AB两人浇一次水,不用if判断保证两人浇水次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public int minimumRefill(int[] plants, int capacityA, int capacityB) {
int ans = 0;
int a = capacityA;
int b = capacityB;
int i = 0;
int j = plants.length - 1;
while (i < j) {
// Alice 给植物 i 浇水
if (a < plants[i]) {
// 没有足够的水,重新灌满水罐
ans++;
a = capacityA;
}
a -= plants[i++];
// Bob 给植物 j 浇水
if (b < plants[j]) {
// 没有足够的水,重新灌满水罐
ans++;
b = capacityB;
}
b -= plants[j--];
}
// 如果 Alice 和 Bob 到达同一株植物,那么当前水罐中水更多的人会给这株植物浇水
if (i == j && Math.max(a, b) < plants[i]) {
// 没有足够的水,重新灌满水罐
ans++;
}
return ans;
}
}

相向双指针总结

  • 相向双指针也叫左右指针

  • 核心思想:左指针从左往右移动,右指针从右往左移动,根据当前结果决定移动哪一个指针,从而快速缩小搜索范围。

  • 常用于有序数组,或者要从数组两端进行处理

  • 模板:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int left = 0;
    int right = nums.length - 1;

    while (left < right) {

    // 根据 nums[left] 和 nums[right] 判断

    if (条件1) {
    left++;
    } else if (条件2) {
    right--;
    } else {
    // 找到答案
    }
    }
  • 去重思路:

    1. 固定指针去重:当前值 == 前一个值 → continue

    2. left 去重:先 left++,再和 left - 1 比

    3. right 去重:先 right–,再和 right + 1 比

      (left和right去重先++/–是要从正确答案中去重)

  • 常见剪枝:

    1. 排序后,能组成的最小的和已经大于target,后面只会更大,直接结束
    2. 排序后,能组成的最大的和已经小于target,前面只会更小,直接结束