从零开始写算法——双指针-移动零 & 盛最多水的容器
在刷 LeetCode 的过程中,“双指针”几乎是算法题里使用频率最高的技巧之一。
今天我们就通过两道经典题目,彻底掌握双指针思想:
-
283. 移动零 Move Zeroes
-
11. 盛最多水的容器 Container With Most Water
一、283. 移动零(Move Zeroes)
题目描述
给定一个数组 nums,将所有的 0 移动到数组末尾,同时保持非零元素的相对顺序。
必须在 原数组上操作(in-place),不能使用额外的数组空间。
解法一:两次遍历(好理解)
步骤:
-
把所有非零按顺序放到数组前部
-
把剩下的位置全部填 0
代码:
int slow = 0;
for (int x : nums)
if (x != 0) nums[slow++] = x;
while (slow < nums.size())
nums[slow++] = 0;
虽然简单,但需要遍历两次。
解法二:双指针,一次遍历(最推荐)
简化版思路如下:
-
慢指针 slow 指向下一个“应该放非零元素”的位置
-
快指针 fast 负责遍历数组
-
fast 遇到非零 → 与 slow 交换 → slow++
-
fast 遇到 0 → 不做任何事,因为 slow 指向的是“应该被覆盖的位置”
👉 为什么交换后 slow 仍然指向空位?
因为:
-
slow 之前一定是 0(否则它早就被覆盖了)
-
fast 遇到非 0,把它交换到 slow 的位置
-
fast 所在位置被换成 0
-
所以 slow++ 后新的 slow 依然指向 “未处理的位置”,保持空位含义
代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// 思路: 本质就是双指针,慢指针拿来指向0,快指针往前走进行遍历,快指针遇到非0就交换,确保0往后移动。
int slow = 0;
for (int& fast : nums) {
if (fast) {
swap(fast, nums[slow]);
slow++;
}
}
}
};
时间复杂度
-
O(n) 遍历一次
-
交换次数 ≤ 非零个数
空间复杂度
-
O(1)
二、11. 盛最多水的容器(Container With Most Water)
题目描述
给定 height[i] 表示墙的高度,左右两堵墙与 x 轴围成的容器容量为:
面积 = min(height[left], height[right]) * (right - left)
要求求最大容量。
核心思路:双指针夹逼
初始:
-
left = 0
-
right = n - 1
每次计算当前面积后:
-
移动较矮的那根柱子
因为:
容量 = 宽度 * 较矮高度
宽度必然随着移动变小,所以想要变大只能通过变高
→ 必须移动较矮端,才可能获得更高的边界
否则移动高的那一边一点意义都没有。
✔ 正确代码
class Solution {
public:
int maxArea(vector<int>& height) {
// 思路: 双指针进行遍历,同时维护最大值,高度小的那个是限制条件,把它移动
int ans = 0;
int left = 0;
int right = height.size() - 1;
while(left < right) {
int area = (right - left) * min(height[left], height[right]);
ans = max(ans, area);
if (height[left] < height[right]) {
left++;
}
else right--;
}
return ans;
}
};
时间复杂度
-
O(n)。每根柱子最多被操作一次。
空间复杂度
-
O(1),只用几个变量。
总结
| 题目 | 技巧 | 核心思路 |
|---|---|---|
| Move Zeroes | 双指针(快慢指针) | fast 遇到非零就交换到 slow 位置,slow 始终指向下一个“空位” |
| 盛最多水的容器 | 双指针(夹逼) | 宽度缩小不可避免,必须移动较矮侧来尝试增大高度 |
双指针本质上就是:
用两个指针在数组上“控制局面”
一个负责位置
一个负责遍历
或者两个同时移动,缩小范围
只要理解了这种思想,很多题会变得非常简单。
更多推荐


所有评论(0)