本章包括的题目有:
1.移动零
思路解析:
由于要将所有零移动到数组末尾并保持非零元素的相对顺序,只需使用双指针:一个指针 zero 用于定位零的位置,另一个指针 z 用于寻找其后第一个非零元素,找到后交换,重复该过程直到数组末尾。
代码实现:
java
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length;
int zero = 0;
while(zero < n){
while(zero < n && nums[zero] != 0) zero++;
int z = zero + 1;
while(z < n && nums[z] == 0) z++;
if(z >= n)break;
swap(nums,zero,z);
}
}
public void swap(int[] a,int i,int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
时间复杂度O(n)
空间复杂度O(1)
2.盛水最多的容器
思路解析:
由于要在柱状图中找到最大面积(两条线与 x 轴形成的容器盛水量最大),宽度为两线索引差,高度取两线中较矮者,只需使用双指针从两端向中间移动。初始左指针 l = 0,右指针 r = n - 1,计算当前面积并更新最大值。每次移动较矮的指针(因为高度由较矮者决定,移动较矮的指针才有可能遇到更高的柱子来增大面积)。循环直到 l >= r,最终返回最大面积。
代码实现:
java
class Solution {
public int maxArea(int[] height) {
int n = height.length;
int max = 0;
int l = 0,r = n - 1;
while(l < r) {
int value = (r - l) * Math.min(height[l], height[r]);
if (max < value) max = value;
if (height[l] <= height[r]) l++;
else r--;
}
return max;
}
}
时间复杂度O(n)
空间复杂度O(1)
3.三数之和
思路解析:
由于要在无序数组中找到所有不重复的三元组满足和为0,可以定义一个HashSet 来去重。
只需先对数组排序,再固定一个数 nums[i],使用双指针 l = i+1 和 r = len-1 在剩余区间中找两数之和等于 -nums[i]。根据三数之和与0的比较移动指针,找到后添加到结果集合中,最后转为 ArrayList 返回。
代码实现:
java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
Set<List<Integer>> ret = new HashSet<>();
Arrays.sort(nums);
int l, r;
for (int i = 0; i < len - 2; i++) {
l = i + 1;
r = len - 1;
while(l < r){
if(nums[i] + nums[l] + nums[r] > 0) r--;
else if(nums[i] + nums[l] + nums[r] < 0) l++;
else {
ret.add(Arrays.asList(nums[i], nums[l], nums[r]));
l ++; r --;
}
}
}
return new ArrayList<>(ret);
}
}
时间复杂度O(n * n)
空间复杂度O(n)
这种做法虽然能通过,但是我们可以通过一些剪枝来大大提升效率
由于我们排完序了,所以重复元素都在一起,跳过重复元素不仅可以减少外层遍历的次数,还能够天然去重,所以可以直接使用ArrayList
java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
List<List<Integer>> ret = new ArrayList<>();
Arrays.sort(nums);
int l, r;
for (int i = 0; i < len - 2; i++) {
// 剪枝:若当前要判定的数和前一个相同 → 跳过本轮
if (i > 0 && nums[i] == nums[i-1]) continue;
// 剪枝:最小三个数和 > 0 → 后面无解
if (nums[i] + nums[i+1] + nums[i+2] > 0) break;
// 剪枝:当前 i 与最大两个数和 < 0 → 跳过本轮
if (nums[i] + nums[len-2] + nums[len-1] < 0) continue;
l = i + 1;
r = len - 1;
while (l < r) {
int sum = nums[i] + nums[l] + nums[r];
if (sum < 0) l++;
else if (sum > 0) r--;
else {
ret.add(Arrays.asList(nums[i], nums[l], nums[r]));
// 跳过重复的左指针
while (l < r && nums[l] == nums[l+1]) l++;
// 跳过重复的右指针
while (l < r && nums[r] == nums[r-1]) r--;
l++;
r--;
}
}
}
return new ArrayList<>(ret);
}
}
时间复杂度O(n * n)
空间复杂度O(n)
虽然时间复杂度和空间复杂度都没变,但是


时间上效率有明显的提升。
4.接雨水
思路解析:
由于要计算柱状图中能接多少雨水,只需利用"木桶效应":某个位置能接的水量取决于其左右两侧最高柱子的较小值减去自身高度。一种高效做法是先找到全局最高柱子的索引(作为分界),然后从左向右扫描到最高点:维护左侧最高值 leftMax,若当前柱子低于它则累加差值,否则更新 leftMax;再从右向左扫描到最高点,同理维护右侧最高值 rightMax 并累加。两边水量相加即得到总接水量。

如图所示,只需先统计红色圈起来的"金字塔区域的全部方块",再减去黑色方块,得到的就是最终所需要的水量,我们将增减放到同一个循环中就可以简写为water += leftMax(rightMax) - height[i];
代码实现:
java
public class Solution {
public int trap(int[] height) {
if (height == null || height.length < 3) return 0;
int n = height.length;
// 1. 找到最高点的索引(取第一个最高点)
int maxIndex = 0;
for (int i = 1; i < n; i++) {
if (height[i] > height[maxIndex]) {
maxIndex = i;
}
}
int water = 0;
// 2. 从左往右扫描到最高点
int leftMax = 0;
for (int i = 0; i < maxIndex; i++) {
if (height[i] > leftMax) {
leftMax = height[i];
} else {
water += leftMax - height[i];
}
}
// 3. 从右往左扫描到最高点
int rightMax = 0;
for (int i = n - 1; i > maxIndex; i--) {
if (height[i] > rightMax) {
rightMax = height[i];
} else {
water += rightMax - height[i];
}
}
return water;
}
}
时间复杂度:O(n),但总遍历次数为两次
空间复杂度:O(1)
补充:
此题可以用双指针减少一次遍历的次数,只需使用双指针从两端向中间遍历。维护左边最大值 leftMax和右边最大值 rightMax。对于当前指针所指位置,哪边的高度更小,就处理哪边:若当前高度大于等于对应侧的最大值,则更新最大值;否则累加当前侧最大值与当前高度的差值(即该位置能接的雨水量)。移动处理过的指针,直到两指针相遇,总水量即为答案。没有刚才的解法直观但是效率更高。
java
public class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
int ans = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
ans += leftMax - height[left];
}
left++;
} else {
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
ans += rightMax - height[right];
}
right--;
}
}
return ans;
}
}
上一章: