DeepSeek LeetCode 3739. 统计主要元素子数组数目 II Java实现

📅 2026/7/27 8:05:53 👁️ 阅读次数 📝 编程学习
DeepSeek    LeetCode 3739. 统计主要元素子数组数目 II Java实现

问题理解

题目:给定整数数组 nums 和目标值 target,返回 nums 中满足 target 是主要元素的非空子数组数量。

主要元素定义:在子数组中出现次数严格大于其长度的一半。

---

核心思路:转换为前缀和问题

将 nums 转换为一个新数组 a:

· 若 nums[i] == target,则 a[i] = 1
· 否则 a[i] = -1

此时,一个子数组中 target 是主要元素 ⇔ 该子数组在 a 中的元素和 严格大于 0。

设前缀和数组 pre,pre[i] 表示 a[0..i-1] 的和(pre[0]=0)。子数组 (l, r] 的和为 pre[r] - pre[l],要求 pre[r] - pre[l] > 0,即 pre[l] < pre[r]。

问题转化为:对每个位置 r,统计前面有多少个 l 满足 pre[l] < pre[r],累加即为答案。

---

Java 实现(O(n log n),有序列表)

```java
import java.util.*;

class Solution {
public long countMajoritySubarrays(int[] nums, int target) {
// 有序列表,维护所有已出现的前缀和
List<Integer> sortedPrefixes = new ArrayList<>();
sortedPrefixes.add(0); // pre[0] = 0

long ans = 0;
int pre = 0; // 当前前缀和

for (int num : nums) {
pre += (num == target ? 1 : -1);
// 在有序列表中查找第一个 >= pre 的位置
// 该位置之前的所有前缀和都 < pre
int idx = Collections.binarySearch(sortedPrefixes, pre);
if (idx < 0) {
idx = -idx - 1;
}
ans += idx;
// 将当前前缀和插入到有序列表中
sortedPrefixes.add(idx, pre);
}

return ans;
}
}
```

复杂度:时间 O(n log n),空间 O(n)。

---

更优实现(O(n),计数数组)AC

由于前缀和取值范围为 [-n, n],可用计数数组实现 O(1) 查询:

```java
class Solution {
public long countMajoritySubarrays(int[] nums, int target) {
int n = nums.length;
int offset = n + 1; // 将范围 [-n, n] 映射到 [1, 2n+1]
long[] cnt = new long[2 * n + 3];
long[] acc = new long[2 * n + 3];

int pre = offset; // 初始前缀和为0,映射到 offset
cnt[pre] = 1;
acc[pre] = 1;

long ans = 0;
for (int num : nums) {
pre += (num == target ? 1 : -1);
// cnt[pre] 是当前前缀和出现的次数(包括当前这次)
// acc[pre-1] 是所有小于当前前缀和的前缀和出现次数之和
acc[pre] = acc[pre - 1] + ++cnt[pre];
ans += acc[pre - 1];
}
return ans;
}
}
```

复杂度:时间 O(n),空间 O(n)。

---

示例验证

以 nums = [1,2,2,3], target = 2 为例:

· 转换后:[-1, 1, 1, -1]
· 前缀和:0, -1, 0, 1, 0
· 统计 pre[l] < pre[r] 的对数:
· r=1: pre= -1,前面 < -1 的没有 → 0
· r=2: pre=0,前面 < 0 的有 -1 → 1
· r=3: pre=1,前面 < 1 的有 -1, 0, 0 → 3
· r=4: pre=0,前面 < 0 的有 -1 → 1
· 总计 = 0 + 1 + 3 + 1 = 5 ✅