返回 LeetCode 刷题

Markdown File

双指针法

双指针法.md

1一、相向双指针
21. 前提条件
3
4相向双指针一般用于:
5
6数组、字符串、链表这类线性结构。
7
8最常见是数组和字符串。
9
10它的基本形式是:
11
12left 从左边开始;
13right 从右边开始;
14两个指针向中间靠拢。
15
16代码形式:
17
18int left = 0;
19int right = n - 1;
20
21while (left < right) {
22 // 处理逻辑
23}
242. 核心概念
25
26相向双指针的核心不是“两个指针乱走”,而是:
27
28每移动一次指针,都能排除一部分不可能的答案。
29
30所以它一般要求题目中有某种规律,例如:
31
321. 数组有序
332. 左右边界决定答案
343. 面积受短板限制
354. 字符串左右对称
365. 左右最大值决定当前位置结果
373. 固定模板
38int left = 0;
39int right = n - 1;
40
41while (left < right) {
42 if (条件1) {
43 left++;
44 } else if (条件2) {
45 right--;
46 } else {
47 // 找到答案或者更新答案
48 }
49}
50
51更通用地说:
52
531. 初始化 left 和 right
542. while(left < right)
553. 根据当前 left 和 right 计算结果
564. 根据规律移动其中一个指针
574. 常见移动规律
58情况一:有序数组求和
59sum 太小,left++
60sum 太大,right--
61情况二:盛最多水的容器
62左边短,left++
63右边短,right--
64情况三:接雨水
65左边低,处理 left
66右边低,处理 right
67情况四:判断回文
68左右相等,left++,right--
69左右不等,直接失败
705. 易错点
71易错点 1:不是所有数组题都能用双指针
72
73如果没有规律,不能乱用。
74
75比如无序数组中直接这样写是错的:
76
77int left = 0;
78int right = numsSize - 1;
79
80然后直接判断:
81
82nums[left] + nums[right]
83
84因为原数组无序,left++ 或 right-- 没有明确意义。
85
86易错点 2:循环条件一般是 left < right
87
88因为相向双指针通常要求两个不同位置。
89
90while (left < right)
91
92如果写成:
93
94while (left <= right)
95
96可能会把同一个元素用两次。
97
98易错点 3:每轮至少移动一个指针
99
100否则会死循环。
101
102错误:
103
104while (left < right) {
105 int sum = nums[left] + nums[right];
106
107 if (sum == target) {
108 // 忘记 return 或 break
109 }
110}
111二、LeetCode 1:两数之和
1121. 前提条件
113
114题目给:
115数组 nums
116目标值 target
117要求:
118
119找两个不同位置的数,使它们的和等于 target。
120
121返回:
122
123这两个数在原数组中的下标。
124
125注意重点:
126
127返回的是原数组下标,不是排序后的下标。
1282. 核心概念
129
130LeetCode 第一题本身最常用的方法是:
131
132哈希表
133
134但是如果想用相向双指针,也可以。
135
136不过要满足一个前提:
137
138数组必须有序。
139
140所以双指针版本的思路是:
141
142先排序,再用 left 和 right 找 target。
143
144但是排序会打乱下标,所以还要保存:
145
146原始下标 index。
1473. 为什么排序后可以双指针?
148
149假设排序后:
150
151nums = [2, 7, 11, 15]
152target = 9
153
154初始化:
155
156left 指向最小值 2
157right 指向最大值 15
158
159当前和:
160
1612 + 15 = 17
162
163太大了,所以要让和变小。
164
165因为数组有序,右边数字更大,所以移动:
166
167right--;
168
169如果当前和太小:
170
171sum < target
172
173说明要让和变大,所以移动:
174
175left++;
176
177所以规律是:
178
179sum < target,left++
180sum > target,right--
181sum == target,找到答案
1824. 固定模板
183int left = 0;
184int right = numsSize - 1;
185
186while (left < right) {
187 int sum = nums[left] + nums[right];
188
189 if (sum == target) {
190 // 找到答案
191 } else if (sum < target) {
192 left++;
193 } else {
194 right--;
195 }
196}
197
198但是第一题不能直接这么写,因为要返回原下标。
199
200所以实际需要保存:
201
202struct Element {
203 int val;
204 int index;
205};
206
207其中:
208
209val:元素的值
210index:元素在原数组中的下标
2115. C 语言模板:排序 + 双指针
212#include <stdlib.h>
213
214struct Element {
215 int val;
216 int index;
217};
218
219int cmp(const void* a, const void* b) {
220 const struct Element* x = (const struct Element*)a;
221 const struct Element* y = (const struct Element*)b;
222
223 if (x->val < y->val) {
224 return -1;
225 } else if (x->val > y->val) {
226 return 1;
227 } else {
228 return 0;
229 }
230}
231
232int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
233 struct Element* arr = malloc(numsSize * sizeof(struct Element));
234
235 for (int i = 0; i < numsSize; i++) {
236 arr[i].val = nums[i];
237 arr[i].index = i;
238 }
239
240 qsort(arr, numsSize, sizeof(struct Element), cmp);
241
242 int left = 0;
243 int right = numsSize - 1;
244
245 int* result = malloc(2 * sizeof(int));
246
247 while (left < right) {
248 int sum = arr[left].val + arr[right].val;
249
250 if (sum == target) {
251 result[0] = arr[left].index;
252 result[1] = arr[right].index;
253 *returnSize = 2;
254
255 free(arr);
256 return result;
257 } else if (sum < target) {
258 left++;
259 } else {
260 right--;
261 }
262 }
263
264 free(arr);
265 free(result);
266 *returnSize = 0;
267 return NULL;
268}
2696. 易错点
270易错点 1:不能排序后直接返回 left 和 right
271
272错误:
273
274result[0] = left;
275result[1] = right;
276
277因为这是排序后的下标,不是原数组下标。
278
279应该返回:
280
281result[0] = arr[left].index;
282result[1] = arr[right].index;
283易错点 2:比较函数不要直接相减
284
285不推荐:
286
287return x->val - y->val;
288
289因为可能整数溢出。
290
291推荐:
292
293if (x->val < y->val) return -1;
294if (x->val > y->val) return 1;
295return 0;
296易错点 3:返回数组必须 malloc
297
298LeetCode 提示:
299
300/**
301 * Note: The returned array must be malloced, assume caller calls free().
302 */
303
304意思是:
305
306返回的数组必须是 malloc 申请的。
307
308错误:
309
310int result[2];
311return result;
312
313因为局部数组函数结束后就失效了。
314
315正确:
316
317int* result = malloc(2 * sizeof(int));
318return result;
3197. 复杂度
320
321排序 + 双指针:
322
323时间复杂度:O(n log n)
324空间复杂度:O(n)
325
326如果用哈希表:
327
328时间复杂度:O(n)
329空间复杂度:O(n)
330
331所以第一题最优解通常是哈希表,但排序双指针很适合练相向双指针思想。
332
3338. 题型总结
334
335两数之和的双指针规律:
336
337前提:数组有序。
338目标:找两个数的和等于 target。
339移动:
340 sum < target,说明和太小,left++
341 sum > target,说明和太大,right--
342 sum == target,找到答案
343
344一句话:
345
346两数之和的相向双指针,本质是利用有序数组的单调性。
347三、LeetCode 11:盛最多水的容器
3481. 前提条件
349
350题目给你一个数组:height[i]
351
352表示第 i 个位置有一条高度为 height[i] 的竖线。
353
354要你选择两条竖线,使它们和 x 轴围成的容器最多能装多少水。
355
3562. 核心概念
357
358选择两个位置:
359
360left 和 right
361
362容器的宽度是:
363
364right - left
365
366容器的高度取决于短板:
367
368min(height[left], height[right])
369
370所以面积是:
371
372面积 = 宽度 × 短板高度
373
374也就是:
375
376area = (right - left) * min(height[left], height[right]);
377
378注意:
379
380容器能装多少水,不取决于高的那边,而取决于矮的那边。
3813. 为什么用相向双指针?
382
383一开始:
384
385left = 0;
386right = heightSize - 1;
387
388此时宽度最大。
389
390然后每次计算当前面积,更新最大值。
391
392关键是移动哪一边。
393
394如果:
395
396height[left] < height[right]
397
398说明左边是短板。
399
400此时如果移动右边:
401
402宽度变小;
403短板仍然可能是左边;
404面积很难变大。
405
406所以应该移动短板:
407
408left++;
409
410同理,如果右边更短:
411
412right--;
4134. 固定模板
414int maxArea(int* height, int heightSize) {
415 int left = 0;
416 int right = heightSize - 1;
417 int max = 0;
418
419 while (left < right) {
420 int width = right - left;
421
422 int h;
423 if (height[left] < height[right]) {
424 h = height[left];
425 } else {
426 h = height[right];
427 }
428
429 int area = width * h;
430
431 if (area > max) {
432 max = area;
433 }
434
435 if (height[left] < height[right]) {
436 left++;
437 } else {
438 right--;
439 }
440 }
441
442 return max;
443}
4445. 移动规律
445左边短,left++
446右边短,right--
447一样高,移动哪边都可以
448
449口诀:谁短移动谁。
4506. 为什么不是移动高的那边?
451
452假设:
453
454height[left] = 3
455height[right] = 10
456
457当前面积的高度只能是:3
458
459因为短板是左边。
460
461如果移动右边,宽度会变小,而左边短板还是 3,所以不可能通过移动右边突破短板限制。
462
463真正可能让面积变大的方式是:
464
465移动短板,尝试找到更高的边界。
4667. 易错点
467易错点 1:面积高度取短板
468
469错误:
470
471area = (right - left) * height[right];
472
473或者:
474
475area = (right - left) * height[left];
476
477正确:
478
479area = (right - left) * min(height[left], height[right]);
480易错点 2:移动高的那边
481
482错误理解:谁高移动谁。
483
484正确:谁短移动谁。
485
486因为短板决定容器高度。
487
488易错点 3:先移动再算面积
489
490应该先用当前 left 和 right 算面积,再移动指针。
491
492正确顺序:
493
4941. 计算当前面积
4952. 更新最大值
4963. 移动短板
4978. 复杂度
498时间复杂度:O(n)
499空间复杂度:O(1)
500
501因为 left 和 right 一共最多走 n 次。
502
5039. 题型总结
504
505盛最多水的容器:
506
507目标:选两个柱子,使容器面积最大。
508面积:宽度 × 短板高度。
509移动:谁短移动谁。
510原因:只有移动短板,才可能提高容器高度。
511
512一句话:第 11 题的相向双指针,本质是利用“短板决定容量”的规律排除答案。
513
514四、LeetCode 42:接雨水
515
5161. 前提条件
517
518题目给一个数组:
519
520height[i]
521
522表示每个位置的柱子高度。
523
524下雨后,柱子之间可能会接住水。
525
526要求:
527
528计算总共能接多少水。
5292. 核心概念
530
531对于某个位置 i,它能接多少水,取决于:
532
533左边最高的柱子 leftMax
534右边最高的柱子 rightMax
535
536当前位置最多能接:
537
538min(leftMax, rightMax) - height[i]
539
540如果这个值小于 0,就说明接不了水。
541
542核心规律:
543
544一个位置能接水,必须左右两边都有比它高的边界。
5453. 为什么可以用双指针?
546
547普通思路是:
548
549对每个位置分别找左边最高和右边最高。
550
551这样比较麻烦。
552
553双指针做法是同时维护:
554
555leftMax:从左边走到当前位置遇到的最高柱子
556rightMax:从右边走到当前位置遇到的最高柱子
557
558每次处理较低的一边。
559
560原因:
561
562如果 height[left] < height[right],
563说明 left 这一侧的接水量主要由 leftMax 决定;
564右边至少有一个比它高的边界存在。
565
566所以可以放心处理 left。
567
568同理,如果右边低,就处理 right。
569
5704. 固定模板
571int trap(int* height, int heightSize) {
572 int left = 0;
573 int right = heightSize - 1;
574
575 int leftMax = 0;
576 int rightMax = 0;
577
578 int water = 0;
579
580 while (left < right) {
581 if (height[left] < height[right]) {
582 if (height[left] >= leftMax) {
583 leftMax = height[left];
584 } else {
585 water += leftMax - height[left];
586 }
587
588 left++;
589 } else {
590 if (height[right] >= rightMax) {
591 rightMax = height[right];
592 } else {
593 water += rightMax - height[right];
594 }
595
596 right--;
597 }
598 }
599
600 return water;
601}
602如果 height[left] < height[right]:
603 说明右边墙更高,左边能不能接水由 leftMax 决定。
604 所以处理 left。
605
606如果 height[left] >= height[right]:
607 说明左边墙更高,右边能不能接水由 rightMax 决定。
608 所以处理 right。
6095. 移动规律
610左边低,处理 left,然后 left++
611右边低,处理 right,然后 right--
612
613不是简单地“谁短移动谁”,而是:
614
615谁低,说明谁那边的边界更确定,先处理谁。
6166. 接雨水和盛水容器的区别
617题目 目标 计算方式 移动规律
618第 11 题 盛最多水 选两个柱子 算一个容器面积 谁短移动谁
619第 42 题 接雨水 计算所有坑的水 每个位置累加水量 谁低处理谁
620
621最关键区别:
622
623第 11 题是选两个边界。
624第 42 题是每个位置都要算。
6257. 易错点
626易错点 1:把第 11 题和第 42 题混在一起
627
628第 11 题:
629
630只看两个柱子形成的面积。
631
632第 42 题:
633
634看所有柱子之间能积多少水。
635易错点 2:接雨水不是直接算左右两边高度
636
637错误理解:
638
639当前位置水量 = 左边柱子高度 - 当前高度
640
641正确是:
642
643当前位置水量 = min(左边最高, 右边最高) - 当前高度
644易错点 3:忘记维护 leftMax 和 rightMax
645
646如果只看当前左右柱子:
647
648height[left]
649height[right]
650
651是不够的。
652
653接雨水看的是:
654
655左边历史最高
656右边历史最高
657
658所以要维护:
659
660leftMax
661rightMax
6628. 复杂度
663时间复杂度:O(n)
664空间复杂度:O(1)
6659. 题型总结
666
667接雨水:
668
669目标:计算所有位置能接的水量总和。
670核心:每个位置能接多少水,取决于左右最大边界的较小值。
671双指针:维护 leftMax 和 rightMax。
672移动:哪边低,先处理哪边。
673
674
675第 42 题的相向双指针,本质是用 leftMax 和 rightMax 动态确定每个位置的接水量。
676五、总结:相向双指针题型对比
677题型 前提 计算什么 移动谁
678两数之和 有序数组 两数和 sum 小 left++,sum 大 right--
679盛最多水 左右边界成容器 面积最大值 谁短移动谁
680接雨水 左右最大边界 每个位置水量 谁低处理谁
681回文判断 左右对称 字符是否相等 相等一起移动,不等返回失败
Rendered Preview

一、相向双指针

  1. 前提条件

相向双指针一般用于:

数组、字符串、链表这类线性结构。

最常见是数组和字符串。

它的基本形式是:

left 从左边开始;
right 从右边开始;
两个指针向中间靠拢。

代码形式:

int left = 0;
int right = n - 1;

while (left < right) {
// 处理逻辑
}
2. 核心概念

相向双指针的核心不是“两个指针乱走”,而是:

每移动一次指针,都能排除一部分不可能的答案。

所以它一般要求题目中有某种规律,例如:

  1. 数组有序
  2. 左右边界决定答案
  3. 面积受短板限制
  4. 字符串左右对称
  5. 左右最大值决定当前位置结果
  6. 固定模板
    int left = 0;
    int right = n - 1;

while (left < right) {
if (条件1) {
left++;
} else if (条件2) {
right--;
} else {
// 找到答案或者更新答案
}
}

更通用地说:

  1. 初始化 left 和 right
  2. while(left < right)
  3. 根据当前 left 和 right 计算结果
  4. 根据规律移动其中一个指针
  5. 常见移动规律
    情况一:有序数组求和
    sum 太小,left++
    sum 太大,right--
    情况二:盛最多水的容器
    左边短,left++
    右边短,right--
    情况三:接雨水
    左边低,处理 left
    右边低,处理 right
    情况四:判断回文
    左右相等,left++,right--
    左右不等,直接失败
  6. 易错点
    易错点 1:不是所有数组题都能用双指针

如果没有规律,不能乱用。

比如无序数组中直接这样写是错的:

int left = 0;
int right = numsSize - 1;

然后直接判断:

nums[left] + nums[right]

因为原数组无序,left++ 或 right-- 没有明确意义。

易错点 2:循环条件一般是 left < right

因为相向双指针通常要求两个不同位置。

while (left < right)

如果写成:

while (left <= right)

可能会把同一个元素用两次。

易错点 3:每轮至少移动一个指针

否则会死循环。

错误:

while (left < right) {
int sum = nums[left] + nums[right];

if (sum == target) {
    // 忘记 return 或 break
}

}
二、LeetCode 1:两数之和

  1. 前提条件

题目给:
数组 nums
目标值 target
要求:

找两个不同位置的数,使它们的和等于 target。

返回:

这两个数在原数组中的下标。

注意重点:

返回的是原数组下标,不是排序后的下标。
2. 核心概念

LeetCode 第一题本身最常用的方法是:

哈希表

但是如果想用相向双指针,也可以。

不过要满足一个前提:

数组必须有序。

所以双指针版本的思路是:

先排序,再用 left 和 right 找 target。

但是排序会打乱下标,所以还要保存:

原始下标 index。
3. 为什么排序后可以双指针?

假设排序后:

nums = [2, 7, 11, 15]
target = 9

初始化:

left 指向最小值 2
right 指向最大值 15

当前和:

2 + 15 = 17

太大了,所以要让和变小。

因为数组有序,右边数字更大,所以移动:

right--;

如果当前和太小:

sum < target

说明要让和变大,所以移动:

left++;

所以规律是:

sum < target,left++
sum > target,right--
sum == target,找到答案
4. 固定模板
int left = 0;
int right = numsSize - 1;

while (left < right) {
int sum = nums[left] + nums[right];

if (sum == target) {
    // 找到答案
} else if (sum < target) {
    left++;
} else {
    right--;
}

}

但是第一题不能直接这么写,因为要返回原下标。

所以实际需要保存:

struct Element {
int val;
int index;
};

其中:

val:元素的值
index:元素在原数组中的下标
5. C 语言模板:排序 + 双指针
#include <stdlib.h>

struct Element {
int val;
int index;
};

int cmp(const void* a, const void* b) {
const struct Element* x = (const struct Element*)a;
const struct Element* y = (const struct Element*)b;

if (x->val < y->val) {
    return -1;
} else if (x->val > y->val) {
    return 1;
} else {
    return 0;
}

}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
struct Element* arr = malloc(numsSize * sizeof(struct Element));

for (int i = 0; i < numsSize; i++) {
    arr[i].val = nums[i];
    arr[i].index = i;
}

qsort(arr, numsSize, sizeof(struct Element), cmp);

int left = 0;
int right = numsSize - 1;

int* result = malloc(2 * sizeof(int));

while (left < right) {
    int sum = arr[left].val + arr[right].val;

    if (sum == target) {
        result[0] = arr[left].index;
        result[1] = arr[right].index;
        *returnSize = 2;

        free(arr);
        return result;
    } else if (sum < target) {
        left++;
    } else {
        right--;
    }
}

free(arr);
free(result);
*returnSize = 0;
return NULL;

}
6. 易错点
易错点 1:不能排序后直接返回 left 和 right

错误:

result[0] = left;
result[1] = right;

因为这是排序后的下标,不是原数组下标。

应该返回:

result[0] = arr[left].index;
result[1] = arr[right].index;
易错点 2:比较函数不要直接相减

不推荐:

return x->val - y->val;

因为可能整数溢出。

推荐:

if (x->val < y->val) return -1;
if (x->val > y->val) return 1;
return 0;
易错点 3:返回数组必须 malloc

LeetCode 提示:

/**

  • Note: The returned array must be malloced, assume caller calls free().
    */

意思是:

返回的数组必须是 malloc 申请的。

错误:

int result[2];
return result;

因为局部数组函数结束后就失效了。

正确:

int* result = malloc(2 * sizeof(int));
return result;
7. 复杂度

排序 + 双指针:

时间复杂度:O(n log n)
空间复杂度:O(n)

如果用哈希表:

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

所以第一题最优解通常是哈希表,但排序双指针很适合练相向双指针思想。

  1. 题型总结

两数之和的双指针规律:

前提:数组有序。
目标:找两个数的和等于 target。
移动:
sum < target,说明和太小,left++
sum > target,说明和太大,right--
sum == target,找到答案

一句话:

两数之和的相向双指针,本质是利用有序数组的单调性。
三、LeetCode 11:盛最多水的容器

  1. 前提条件

题目给你一个数组:height[i]

表示第 i 个位置有一条高度为 height[i] 的竖线。

要你选择两条竖线,使它们和 x 轴围成的容器最多能装多少水。

  1. 核心概念

选择两个位置:

left 和 right

容器的宽度是:

right - left

容器的高度取决于短板:

min(height[left], height[right])

所以面积是:

面积 = 宽度 × 短板高度

也就是:

area = (right - left) * min(height[left], height[right]);

注意:

容器能装多少水,不取决于高的那边,而取决于矮的那边。
3. 为什么用相向双指针?

一开始:

left = 0;
right = heightSize - 1;

此时宽度最大。

然后每次计算当前面积,更新最大值。

关键是移动哪一边。

如果:

height[left] < height[right]

说明左边是短板。

此时如果移动右边:

宽度变小;
短板仍然可能是左边;
面积很难变大。

所以应该移动短板:

left++;

同理,如果右边更短:

right--;
4. 固定模板
int maxArea(int* height, int heightSize) {
int left = 0;
int right = heightSize - 1;
int max = 0;

while (left < right) {
    int width = right - left;

    int h;
    if (height[left] < height[right]) {
        h = height[left];
    } else {
        h = height[right];
    }

    int area = width * h;

    if (area > max) {
        max = area;
    }

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

return max;

}
5. 移动规律
左边短,left++
右边短,right--
一样高,移动哪边都可以

口诀:谁短移动谁。
6. 为什么不是移动高的那边?

假设:

height[left] = 3
height[right] = 10

当前面积的高度只能是:3

因为短板是左边。

如果移动右边,宽度会变小,而左边短板还是 3,所以不可能通过移动右边突破短板限制。

真正可能让面积变大的方式是:

移动短板,尝试找到更高的边界。
7. 易错点
易错点 1:面积高度取短板

错误:

area = (right - left) * height[right];

或者:

area = (right - left) * height[left];

正确:

area = (right - left) * min(height[left], height[right]);
易错点 2:移动高的那边

错误理解:谁高移动谁。

正确:谁短移动谁。

因为短板决定容器高度。

易错点 3:先移动再算面积

应该先用当前 left 和 right 算面积,再移动指针。

正确顺序:

  1. 计算当前面积
  2. 更新最大值
  3. 移动短板
  4. 复杂度
    时间复杂度:O(n)
    空间复杂度:O(1)

因为 left 和 right 一共最多走 n 次。

  1. 题型总结

盛最多水的容器:

目标:选两个柱子,使容器面积最大。
面积:宽度 × 短板高度。
移动:谁短移动谁。
原因:只有移动短板,才可能提高容器高度。

一句话:第 11 题的相向双指针,本质是利用“短板决定容量”的规律排除答案。

四、LeetCode 42:接雨水

  1. 前提条件

题目给一个数组:

height[i]

表示每个位置的柱子高度。

下雨后,柱子之间可能会接住水。

要求:

计算总共能接多少水。
2. 核心概念

对于某个位置 i,它能接多少水,取决于:

左边最高的柱子 leftMax
右边最高的柱子 rightMax

当前位置最多能接:

min(leftMax, rightMax) - height[i]

如果这个值小于 0,就说明接不了水。

核心规律:

一个位置能接水,必须左右两边都有比它高的边界。
3. 为什么可以用双指针?

普通思路是:

对每个位置分别找左边最高和右边最高。

这样比较麻烦。

双指针做法是同时维护:

leftMax:从左边走到当前位置遇到的最高柱子
rightMax:从右边走到当前位置遇到的最高柱子

每次处理较低的一边。

原因:

如果 height[left] < height[right],
说明 left 这一侧的接水量主要由 leftMax 决定;
右边至少有一个比它高的边界存在。

所以可以放心处理 left。

同理,如果右边低,就处理 right。

  1. 固定模板
    int trap(int* height, int heightSize) {
    int left = 0;
    int right = heightSize - 1;

    int leftMax = 0;
    int rightMax = 0;

    int water = 0;

    while (left < right) {
    if (height[left] < height[right]) {
    if (height[left] >= leftMax) {
    leftMax = height[left];
    } else {
    water += leftMax - height[left];
    }

         left++;
     } else {
         if (height[right] >= rightMax) {
             rightMax = height[right];
         } else {
             water += rightMax - height[right];
         }
    
         right--;
     }
    

    }

    return water;
    }
    如果 height[left] < height[right]:
    说明右边墙更高,左边能不能接水由 leftMax 决定。
    所以处理 left。

如果 height[left] >= height[right]:
说明左边墙更高,右边能不能接水由 rightMax 决定。
所以处理 right。
5. 移动规律
左边低,处理 left,然后 left++
右边低,处理 right,然后 right--

不是简单地“谁短移动谁”,而是:

谁低,说明谁那边的边界更确定,先处理谁。
6. 接雨水和盛水容器的区别
题目 目标 计算方式 移动规律
第 11 题 盛最多水 选两个柱子 算一个容器面积 谁短移动谁
第 42 题 接雨水 计算所有坑的水 每个位置累加水量 谁低处理谁

最关键区别:

第 11 题是选两个边界。
第 42 题是每个位置都要算。
7. 易错点
易错点 1:把第 11 题和第 42 题混在一起

第 11 题:

只看两个柱子形成的面积。

第 42 题:

看所有柱子之间能积多少水。
易错点 2:接雨水不是直接算左右两边高度

错误理解:

当前位置水量 = 左边柱子高度 - 当前高度

正确是:

当前位置水量 = min(左边最高, 右边最高) - 当前高度
易错点 3:忘记维护 leftMax 和 rightMax

如果只看当前左右柱子:

height[left]
height[right]

是不够的。

接雨水看的是:

左边历史最高
右边历史最高

所以要维护:

leftMax
rightMax
8. 复杂度
时间复杂度:O(n)
空间复杂度:O(1)
9. 题型总结

接雨水:

目标:计算所有位置能接的水量总和。
核心:每个位置能接多少水,取决于左右最大边界的较小值。
双指针:维护 leftMax 和 rightMax。
移动:哪边低,先处理哪边。

第 42 题的相向双指针,本质是用 leftMax 和 rightMax 动态确定每个位置的接水量。
五、总结:相向双指针题型对比
题型 前提 计算什么 移动谁
两数之和 有序数组 两数和 sum 小 left++,sum 大 right--
盛最多水 左右边界成容器 面积最大值 谁短移动谁
接雨水 左右最大边界 每个位置水量 谁低处理谁
回文判断 左右对称 字符是否相等 相等一起移动,不等返回失败