前言:
第一题的思路:用你会发现只有左边与右边是当前指针遍历过的元素中最大的和第二大的才能竞争接最多雨水的资格,为什么那,因为接最多雨水这个问题可以带入矮柱子的角度,如果每个以矮柱子为下线的最大接雨水能被遍历到,那么所有的结果是不是都遍历到了那,那么我们可以看到矮柱子回向里移动,那么他就经历了以他为底的情况,那么怎么找到最大的呢,那么只有去看长度,那么左边会有比他长,但比现在左边短的情况吗,有,当然有,就是在当前遍历过的右柱子里有一个仅次于当前左柱子里的柱子,使当前右柱子遇到左柱子里的进行向前更替,那么确实会有遍历当前柱子没遍历到他最大的情况,但是你反向想一下,既然他会进行更替,它必然经过我说的那种,那么既然比它更大的情况都被经历过了,还需要经历现在这种情况吗,当然不需要,那么只有什么情况才不会出现上面的那种经行逆推的情况,只有我说的最高和次高的情况才不需要逆推。所以矮柱子更替是在寻求更大的值,如果没有,他也不会忽略最大的情况。(简单的来说,移动短柱子,才能增大,因为短柱子拔高下线,这些是合理的解释)
第二题的思路:因为3个位置不相等,i,j,k,,为了将所有的情况都遍历到,那么以最左为起点,设置2个指针,那么进行剪枝,先给数组排序,如果nums[i]>0的话,就回,另一个是nums[i]与nums[i-1]相同就跳,题目要求,接下来寻找所有以该值为nums[i]的情况,进行while循环,那么会发现我们会遇到加起来大于0和小于0的情况,那么相应的右指针--和左指针++就可以,然后遇到相同的情况。先推入,在剪枝,去重,最后直到循环结束。
第3题思路:这道medium的题有点意思,我们可以用set容器来做,他的特点是快熟查找并且不打乱顺序,那么我们可以先定以一个set容器nums._set然后将原数组整个放入该容器中,最后利用指针查找去重,只有当前该nums[i]-1不在容器中时才执行,然后找到后,更新大小,长度,最后得到答案,比前2道更简单,利用了容器思想。
📌 一、盛最多水的容器(Container With Most Water)
🔍 题目回顾
给定 n 条垂线,第 i 条线高度为 height[i],找出两条线,使得它们与 x 轴围成的容器能装最多的水。
容器面积 =
min(height[left], height[right]) * (right - left)
✅ 核心思路(双指针法)
-
初始:左指针
left = 0,右指针right = n-1 -
关键观察:
- 面积由较短边决定。
- 若移动较长边,宽度减小,高度不会超过当前短边 → 面积一定变小。
- 只有移动较短边,才有可能找到更高的边,从而获得更大面积。
-
策略:每次移动较矮的一侧指针,并更新最大面积。
❌ 原描述中“最高和次高”说法不准确。正确逻辑是:贪心地移动短板,因为这是唯一可能增大面积的方向。
💡 JavaScript 实现
javascript
编辑
function maxArea(height) {
let left = 0;
let right = height.length - 1;
let max = 0;
while (left < right) {
const h = Math.min(height[left], height[right]);
const w = right - left;
max = Math.max(max, h * w);
// 移动较矮的一边
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return max;
}
⏱️ 时间复杂度:O(n)
🧠 空间复杂度:O(1)
📌 二、三数之和(3Sum)
🔍 题目回顾
找出所有不重复的三元组 [a, b, c],使得 a + b + c = 0。
✅ 核心思路(排序 + 双指针 + 去重)
-
排序数组:便于使用双指针,并方便去重。
-
固定第一个数
nums[i]:- 若
nums[i] > 0,后续所有数 ≥0,不可能和为 0 → 提前退出。 - 若
nums[i] === nums[i-1],跳过 → 避免重复三元组。
- 若
-
双指针查找:
-
left = i + 1,right = n - 1 -
计算
sum = nums[i] + nums[left] + nums[right]sum === 0→ 记录结果,左右指针同时去重后移动sum < 0→left++sum > 0→right--
-
💡 JavaScript 实现
javascript
编辑
function threeSum(nums) {
const res = [];
nums.sort((a, b) => a - b); // 升序排序
for (let i = 0; i < nums.length - 2; i++) {
// 剪枝1:最小值 > 0,不可能和为0
if (nums[i] > 0) break;
// 剪枝2:跳过重复的 i
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
res.push([nums[i], nums[left], nums[right]]);
// 去重 left 和 right
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return res;
}
⏱️ 时间复杂度:O(n²)
🧠 空间复杂度:O(1)(不计输出空间)
📌 三、最长连续序列(Longest Consecutive Sequence)
🔍 题目回顾
在未排序数组中,找出数字连续的最长序列长度,要求 O(n) 时间。
✅ 核心思路(哈希集合 + 起点判断)
-
将所有数字存入
Set,实现 O(1) 查找。 -
关键技巧:只从“连续序列的起点”开始计数。
- 一个数
x是起点 ⇨x - 1不在集合中。
- 一个数
-
对每个起点,向后
x+1, x+2, ...直到断开,记录长度。
✅ 这样每个元素最多被访问两次(一次作为非起点被跳过,一次作为起点被遍历),总时间 O(n)。
💡 JavaScript 实现
javascript
编辑
function longestConsecutive(nums) {
const numSet = new Set(nums);
let maxLength = 0;
for (const num of numSet) {
// 只有当 num 是连续序列的起点时才开始计数
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentLength = 1;
while (numSet.has(currentNum + 1)) {
currentNum++;
currentLength++;
}
maxLength = Math.max(maxLength, currentLength);
}
}
return maxLength;
}
⏱️ 时间复杂度:O(n)
🧠 空间复杂度:O(n)
📚 总结对比
| 题目 | 核心思想 | 数据结构 | 时间复杂度 | 关键技巧 |
|---|---|---|---|---|
| 盛最多水的容器 | 双指针 + 贪心 | 数组 | O(n) | 移动短板 |
| 三数之和 | 排序 + 双指针 + 去重 | 数组 | O(n²) | 固定一数,双指针夹逼,跳过重复 |
| 最长连续序列 | 哈希集合 + 起点判断 | Set | O(n) | 只从序列起点开始扩展 |
✅ 学习建议
- 双指针是解决“两数/三数之和”、“容器面积”等问题的利器。
- 去重要在移动指针前完成,避免漏解或重复。
- 哈希集合(Set) 能将查找优化到 O(1),特别适合“存在性判断”类问题。
- 面试中这三题都是高频题,务必手写熟练!
如需 LeetCode 链接、动画演示或更多变体题(如 4Sum、接雨水等),也可以告诉我