Kimi LeetCode 2444.统计定界子数组的数目 public long countSubarrays(int[] nums, int minK, int maxK)
这是 LeetCode 2444. Count Subarrays With Fixed Bounds(统计定界子数组的数目)的 Java 实现。
题目理解
求满足以下三个条件的子数组数量:
1. 子数组内的最小值恰好等于 `minK`
2. 子数组内的最大值恰好等于 `maxK`
3. 子数组内的所有元素都在 `[minK, maxK]` 范围内(即没有元素小于 `minK` 或大于 `maxK`)
核心思路:双指针 / 滑动窗口
关键观察:
- 如果子数组中存在元素 `< minK` 或 `> maxK`,则该元素作为分割点,将数组分成多个独立区间
- 在每个有效区间内,我们需要找包含至少一个 `minK` 和至少一个 `maxK` 的子数组数量
具体做法:
- 遍历数组,维护三个关键位置:
- `leftBound`:最近一次遇到非法元素(`< minK` 或 `> maxK`)的位置
- `minPos`:最近一次遇到 `minK` 的位置
- `maxPos`:最近一次遇到 `maxK` 的位置
- 对于当前位置 `i`,以 `i` 结尾的有效子数组数量为 `max(0, min(minPos, maxPos) - leftBound)`
- 累加所有位置的结果即为答案
Java 代码
```java
class Solution {
public long countSubarrays(int[] nums, int minK, int maxK) {
long ans = 0;
// leftBound: 最近一次遇到非法元素的位置
// minPos: 最近一次遇到 minK 的位置
// maxPos: 最近一次遇到 maxK 的位置
int leftBound = -1, minPos = -1, maxPos = -1;
for (int i = 0; i < nums.length; i++) {
// 遇到非法元素,更新左边界
if (nums[i] < minK || nums[i] > maxK) {
leftBound = i;
}
// 遇到 minK,更新 minPos
if (nums[i] == minK) {
minPos = i;
}
// 遇到 maxK,更新 maxPos
if (nums[i] == maxK) {
maxPos = i;
}
// 计算以当前位置 i 结尾的有效子数组数量
// 需要同时包含 minK 和 maxK,且都在 leftBound 之后
int validStart = Math.min(minPos, maxPos);
if (validStart > leftBound) {
ans += validStart - leftBound;
}
}
return ans;
}
}
```
思路详解
```
数组: [1, 3, 5, 2, 7, 5], minK=1, maxK=5
i=0, num=1: leftBound=-1, minPos=0, maxPos=-1
validStart=min(0,-1)=-1, -1 > -1? No, ans += 0
i=1, num=3: leftBound=-1, minPos=0, maxPos=-1
validStart=-1, ans += 0
i=2, num=5: leftBound=-1, minPos=0, maxPos=2
validStart=min(0,2)=0, 0 > -1? Yes, ans += 0-(-1) = 1
// 子数组 [1,3,5] 满足条件
i=3, num=2: leftBound=-1, minPos=0, maxPos=2
validStart=0, ans += 0-(-1) = 1 → ans=2
// 子数组 [1,3,5,2] 包含min=1,max=5? max=5在位置2, min=1在位置0, 满足
// 实际上 [3,5,2] 不包含minK=1,所以这里需要再理解...
等等,上面的计算有问题。让我重新分析:
以 i=3 结尾,validStart=0,leftBound=-1
子数组可以从位置 1, 2, 3 开始?不对,应该是从 leftBound+1 到 validStart 之间的任意位置开始
即 start ∈ [0, 0],只有 start=0,子数组 [1,3,5,2]
检查:min=1, max=5,满足!所以 ans += 1 是对的
i=4, num=7: 非法元素!leftBound=4, minPos=0, maxPos=2
validStart=0, 0 > 4? No, ans += 0
i=5, num=5: leftBound=4, minPos=0, maxPos=5
validStart=0, 0 > 4? No, ans += 0
// 等等,minPos=0 在 leftBound=4 左边,不能用了!
```
发现问题:`minPos` 和 `maxPos` 如果在 `leftBound` 左边,就不应该被计入。
实际上代码逻辑是正确的:`validStart = min(minPos, maxPos)`,如果 `validStart > leftBound` 才累加。在上面的例子中,i=5 时 `validStart = min(0, 5) = 0`,而 `0 > 4` 为 false,所以不累加。
但这里有个问题:当遇到新的非法元素后,之前的 `minPos` 和 `maxPos` 应该被重置吗?
实际上不需要重置,因为 `validStart > leftBound` 这个条件会自动过滤掉 `leftBound` 左边的位置。当 `minPos` 或 `maxPos` 在 `leftBound` 左边时,`validStart` 也会 ≤ `leftBound`,自然不会被计入。
不过为了效率,可以在遇到非法元素时重置 `minPos` 和 `maxPos`,但这不是必须的。
修正后的更清晰版本
```java
class Solution {
public long countSubarrays(int[] nums, int minK, int maxK) {
long ans = 0;
int leftBound = -1; // 最近一次非法元素的位置
int minPos = -1; // 最近一次 minK 的位置
int maxPos = -1; // 最近一次 maxK 的位置
for (int i = 0; i < nums.length; i++) {
int x = nums[i];
// 遇到非法元素,更新左边界,并重置 minPos 和 maxPos
if (x < minK || x > maxK) {
leftBound = i;
minPos = -1;
maxPos = -1;
continue; // 非法元素本身不可能作为任何有效子数组的结尾
}
if (x == minK) minPos = i;
if (x == maxK) maxPos = i;
// 必须同时遇到过 minK 和 maxK
if (minPos != -1 && maxPos != -1) {
// 以 i 结尾的有效子数组,起点可以是 (leftBound, min(minPos, maxPos)] 中的任意位置
ans += Math.min(minPos, maxPos) - leftBound;
}
}
return ans;
}
}
```
复杂度分析
项目 复杂度
时间 O(n) — 单次遍历
空间 O(1) — 只使用常数额外空间
其中 `n <= 10^5`,完全可以通过。
更多推荐

所有评论(0)