Skip to content

3月11日报(双指针2)

约 1010 字大约 3 分钟

日报leetcode

2026-03-11

  • 彻底弄懂了 双指针 的 mid 题目,过程和理解在下面
  • 稍微看了看 JS 为后面做项目打基础
  • 继续看了网络
  • 晚上光玩了。

盛最多水容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量

理解题意

设两指针 i, j,指向水槽板高度分别为 h[i] , h[j] ,此状态下水槽面积为 S(i,j) 。由于可容纳水的高度由两板中的 短板 决定,因此可得如下 面积公式S(i, j) = min(h[i], h[j]) * (j - i)

无论在什么状态下,决定容器高度的永远是最短的那一边,于是有以下情况: 无论是将短板或是长板向内移动,一定会导致宽度 -1 变短:

  • 如果将短板向内移动,水槽的短板 min(h[i], h[j]) 可能会变大,因此下一个水槽的面积可能变大
  • 如果将长板向内移动,水槽的短板min(h[i], h[j]) 可能会不变或变小,因此下一个水槽的面积一定减小

因此,将双指针初始化于水槽的左右两端,循环将短板向内移动一格,更新面积最大值,直到两个指针相遇,跳出算法

代码实现

class Solution {
    public int maxArea(int[] height) {
        int l = 0, r = height.length - 1, ans = 0;
        while (l < r) {
            int area = Math.min(height[l], height[r]) * (r - l);
            ans = Math.max(area, ans);
            if (height[l] <= height[r]) {
                l++;
            } else {
                r--;
            }
        }
        return ans;
    }
}

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。 **注意:**答案中不可以包含重复的三元组

思路解析

首先将数组排序,这样就方便我们去重,还可以根据大小关系移动左右指针。之后进行枚举 nums[i] 将问题变形为 nums[j] + nums[k] = -nums[i]

关于去重

要明确一点是,去重的目的是,不要让同一个数值在同一个位置上出现两次 为什么说 在跳过相同元素时,我们使用的是 if (i > 0 && x == nums[i - 1]) continue; 而不是 nums[n + 1] 第一种去重的是结果集,保证同一个位置不重复尝试相同的数值。第二种 去重的是组合内。会错误地禁止组合内出现重复元素,导致漏解 剩下的看代码

代码实现

class Solution {
	public List<List<Integer>> threeSum(int[] nums) {
		Arrays.sort(nums);
		List<List<Integer>> ans = new ArrayList<>();
		int n = nums.length;
		for (int i = 0; i < n - 2; i++) { // 边界问题,留出两个位置给 j k
			int x = nums[i];
			// -- 去重 --
			if (i > 0 && x == nums[i - 1]) continue; // 去重复值
			if (x + nums[i + 1] + nums[i + 2] > 0) break; 
			// 优化最小值
			// 如果 x 加上后面的最小值还是大于 0 ,证明后面的数只会更大 break
			if (x + nums[n - 2] + nums[n - 1] < 0) continue; 
			// 优化最大值
			// 如果 x 加上最大的两个值仍小于 0,证明目前这个 x 实在是太小了
			// -- 指针部分 --
			int j = i + 1;
			int k = n - 1;
			while (j < k) {
				int s = x + nums[j] + nums[k];
				if (s > 0) {
					k--; // sum 太大 左移动 右指针减小
				} else if (s < 0) {
					j++; // sum 太小 右移动 左指针增大
				} else {
					ans.add(List.of(x ,nums[j], nums[k]));
					
					for (j++; j < k && nums[j] == nums[j - 1]; j++);
					for (k--; k > j && nums[k] == nums[k + 1]; k--);
					// 跳过重复数字
					// 1. 先移动指针,对比上一个数是否与下一个数相等
					// 2. 如果新位置的数与上一位置相等再移动指针
				}
			}
		}
		return ans;
	}
}