LeetCode 题解工作台
子数组中占绝大多数的元素
设计一个数据结构,有效地找到给定子数组的 多数元素 。 子数组的 多数元素 是在子数组中出现 threshold 次数或次数以上的元素。 实现 MajorityChecker 类: MajorityChecker(int[] arr) 会用给定的数组 arr 对 MajorityChecker 初始…
5
题型
4
代码语言
3
相关题
当前训练重点
困难 · 二分·搜索·答案·空间
答案摘要
我们注意到,题目需要我们找出特定区间内可能的众数,考虑使用线段树来维护每个区间内的候选众数以及其出现的次数。 我们定义线段树的每个节点为 `Node`,每个节点包含如下属性:
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 二分·搜索·答案·空间 题型思路
题目描述
设计一个数据结构,有效地找到给定子数组的 多数元素 。
子数组的 多数元素 是在子数组中出现 threshold 次数或次数以上的元素。
实现 MajorityChecker 类:
MajorityChecker(int[] arr)会用给定的数组arr对MajorityChecker初始化。int query(int left, int right, int threshold)返回子数组中的元素arr[left...right]至少出现threshold次数,如果不存在这样的元素则返回-1。
示例 1:
输入: ["MajorityChecker", "query", "query", "query"] [[[1, 1, 2, 2, 1, 1]], [0, 5, 4], [0, 3, 3], [2, 3, 2]] 输出: [null, 1, -1, 2] 解释: MajorityChecker majorityChecker = new MajorityChecker([1,1,2,2,1,1]); majorityChecker.query(0,5,4); // 返回 1 majorityChecker.query(0,3,3); // 返回 -1 majorityChecker.query(2,3,2); // 返回 2
提示:
1 <= arr.length <= 2 * 1041 <= arr[i] <= 2 * 1040 <= left <= right < arr.lengththreshold <= right - left + 12 * threshold > right - left + 1- 调用
query的次数最多为104
解题思路
方法一:线段树 + 摩尔投票 + 二分查找
我们注意到,题目需要我们找出特定区间内可能的众数,考虑使用线段树来维护每个区间内的候选众数以及其出现的次数。
我们定义线段树的每个节点为 Node,每个节点包含如下属性:
l:节点的左端点,下标从 开始。r:节点的右端点,下标从 开始。x:节点的候选众数。cnt:节点的候选众数出现的次数。
线段树主要有以下几个操作:
build(u, l, r):建立线段树。pushup(u):用子节点的信息更新父节点的信息。query(u, l, r):查询区间和。
在主函数的初始化方法中,我们先创建一个线段树,并且用哈希表 记录每个元素在数组中的所有下标。
在 query(left, right, threshold) 方法中,我们直接调用线段树的 query 方法,得到候选众数 。然后使用二分查找,找到 在数组中第一个大于等于 的下标 ,以及第一个大于 的下标 。如果 ,则返回 ,否则返回 。
时间复杂度方面,初始化方法的时间复杂度为 ,查询方法的时间复杂度为 。空间复杂度为 。其中 为数组的长度。
class Node:
__slots__ = ("l", "r", "x", "cnt")
def __init__(self):
self.l = self.r = 0
self.x = self.cnt = 0
class SegmentTree:
def __init__(self, nums):
self.nums = nums
n = len(nums)
self.tr = [Node() for _ in range(n << 2)]
self.build(1, 1, n)
def build(self, u, l, r):
self.tr[u].l, self.tr[u].r = l, r
if l == r:
self.tr[u].x = self.nums[l - 1]
self.tr[u].cnt = 1
return
mid = (l + r) >> 1
self.build(u << 1, l, mid)
self.build(u << 1 | 1, mid + 1, r)
self.pushup(u)
def query(self, u, l, r):
if self.tr[u].l >= l and self.tr[u].r <= r:
return self.tr[u].x, self.tr[u].cnt
mid = (self.tr[u].l + self.tr[u].r) >> 1
if r <= mid:
return self.query(u << 1, l, r)
if l > mid:
return self.query(u << 1 | 1, l, r)
x1, cnt1 = self.query(u << 1, l, r)
x2, cnt2 = self.query(u << 1 | 1, l, r)
if x1 == x2:
return x1, cnt1 + cnt2
if cnt1 >= cnt2:
return x1, cnt1 - cnt2
else:
return x2, cnt2 - cnt1
def pushup(self, u):
if self.tr[u << 1].x == self.tr[u << 1 | 1].x:
self.tr[u].x = self.tr[u << 1].x
self.tr[u].cnt = self.tr[u << 1].cnt + self.tr[u << 1 | 1].cnt
elif self.tr[u << 1].cnt >= self.tr[u << 1 | 1].cnt:
self.tr[u].x = self.tr[u << 1].x
self.tr[u].cnt = self.tr[u << 1].cnt - self.tr[u << 1 | 1].cnt
else:
self.tr[u].x = self.tr[u << 1 | 1].x
self.tr[u].cnt = self.tr[u << 1 | 1].cnt - self.tr[u << 1].cnt
class MajorityChecker:
def __init__(self, arr: List[int]):
self.tree = SegmentTree(arr)
self.d = defaultdict(list)
for i, x in enumerate(arr):
self.d[x].append(i)
def query(self, left: int, right: int, threshold: int) -> int:
x, _ = self.tree.query(1, left + 1, right + 1)
l = bisect_left(self.d[x], left)
r = bisect_left(self.d[x], right + 1)
return x if r - l >= threshold else -1
# Your MajorityChecker object will be instantiated and called as such:
# obj = MajorityChecker(arr)
# param_1 = obj.query(left,right,threshold)
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | and space complexity depend on the approach: frequency maps with binary search give O(k log n) per query, segment trees give O(log n) per query, and random sampling achieves expected O(log n) with extra space for index maps. |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
Can you handle multiple online queries efficiently without scanning each subarray?
- question_mark
What data structures allow you to count element frequencies quickly within a range?
- question_mark
How can binary search be applied over the candidate element positions to validate thresholds?
常见陷阱
外企场景- error
Assuming a linear scan per query is fast enough for large arrays.
- error
Forgetting that the majority element must meet the threshold, not just be most frequent.
- error
Not handling the case when no element satisfies the threshold and returning -1 incorrectly.
进阶变体
外企场景- arrow_right_alt
Find the majority element for dynamic subarrays where elements can be updated between queries.
- arrow_right_alt
Return all elements that appear more than a fraction of the subarray size rather than a fixed threshold.
- arrow_right_alt
Optimize for streaming input where queries arrive in real time and the array is too large to store completely.