Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Design a data structure that efficiently finds the majority element of a given subarray.
The majority element of a subarray is an element that occurs threshold times or more in the subarray.
Implementing the MajorityChecker class:
MajorityChecker(int[] arr) Initializes the instance of the class with the given array arr.int query(int left, int right, int threshold) returns the element in the subarray arr[left...right] that occurs at least threshold times, or -1 if no such element exists.Example 1:
Input ["MajorityChecker", "query", "query", "query"] [[[1, 1, 2, 2, 1, 1]], [0, 5, 4], [0, 3, 3], [2, 3, 2]] Output [null, 1, -1, 2] Explanation MajorityChecker majorityChecker = new MajorityChecker([1, 1, 2, 2, 1, 1]); majorityChecker.query(0, 5, 4); // return 1 majorityChecker.query(0, 3, 3); // return -1 majorityChecker.query(2, 3, 2); // return 2
Constraints:
1 <= arr.length <= 2 * 1041 <= arr[i] <= 2 * 1040 <= left <= right < arr.lengththreshold <= right - left + 12 * threshold > right - left + 1104 calls will be made to query.Problem summary: Design a data structure that efficiently finds the majority element of a given subarray. The majority element of a subarray is an element that occurs threshold times or more in the subarray. Implementing the MajorityChecker class: MajorityChecker(int[] arr) Initializes the instance of the class with the given array arr. int query(int left, int right, int threshold) returns the element in the subarray arr[left...right] that occurs at least threshold times, or -1 if no such element exists.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Design · Segment Tree
["MajorityChecker","query","query","query"] [[[1,1,2,2,1,1]],[0,5,4],[0,3,3],[2,3,2]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1157: Online Majority Element In Subarray
class Node {
int l, r;
int x, cnt;
}
class SegmentTree {
private Node[] tr;
private int[] nums;
public SegmentTree(int[] nums) {
int n = nums.length;
this.nums = nums;
tr = new Node[n << 2];
for (int i = 0; i < tr.length; ++i) {
tr[i] = new Node();
}
build(1, 1, n);
}
private void build(int u, int l, int r) {
tr[u].l = l;
tr[u].r = r;
if (l == r) {
tr[u].x = nums[l - 1];
tr[u].cnt = 1;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
public int[] query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return new int[] {tr[u].x, tr[u].cnt};
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (r <= mid) {
return query(u << 1, l, r);
}
if (l > mid) {
return query(u << 1 | 1, l, r);
}
var left = query(u << 1, l, r);
var right = query(u << 1 | 1, l, r);
if (left[0] == right[0]) {
left[1] += right[1];
} else if (left[1] >= right[1]) {
left[1] -= right[1];
} else {
right[1] -= left[1];
left = right;
}
return left;
}
private void pushup(int u) {
if (tr[u << 1].x == tr[u << 1 | 1].x) {
tr[u].x = tr[u << 1].x;
tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
} else if (tr[u << 1].cnt >= tr[u << 1 | 1].cnt) {
tr[u].x = tr[u << 1].x;
tr[u].cnt = tr[u << 1].cnt - tr[u << 1 | 1].cnt;
} else {
tr[u].x = tr[u << 1 | 1].x;
tr[u].cnt = tr[u << 1 | 1].cnt - tr[u << 1].cnt;
}
}
}
class MajorityChecker {
private SegmentTree tree;
private Map<Integer, List<Integer>> d = new HashMap<>();
public MajorityChecker(int[] arr) {
tree = new SegmentTree(arr);
for (int i = 0; i < arr.length; ++i) {
d.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
}
}
public int query(int left, int right, int threshold) {
int x = tree.query(1, left + 1, right + 1)[0];
int l = search(d.get(x), left);
int r = search(d.get(x), right + 1);
return r - l >= threshold ? x : -1;
}
private int search(List<Integer> arr, int x) {
int left = 0, right = arr.size();
while (left < right) {
int mid = (left + right) >> 1;
if (arr.get(mid) >= x) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
/**
* Your MajorityChecker object will be instantiated and called as such:
* MajorityChecker obj = new MajorityChecker(arr);
* int param_1 = obj.query(left,right,threshold);
*/
// Accepted solution for LeetCode #1157: Online Majority Element In Subarray
type node struct {
l, r, x, cnt int
}
type segmentTree struct {
nums []int
tr []*node
}
type pair struct{ x, cnt int }
func newSegmentTree(nums []int) *segmentTree {
n := len(nums)
tr := make([]*node, n<<2)
for i := range tr {
tr[i] = &node{}
}
t := &segmentTree{nums, tr}
t.build(1, 1, n)
return t
}
func (t *segmentTree) build(u, l, r int) {
t.tr[u].l, t.tr[u].r = l, r
if l == r {
t.tr[u].x = t.nums[l-1]
t.tr[u].cnt = 1
return
}
mid := (l + r) >> 1
t.build(u<<1, l, mid)
t.build(u<<1|1, mid+1, r)
t.pushup(u)
}
func (t *segmentTree) query(u, l, r int) pair {
if t.tr[u].l >= l && t.tr[u].r <= r {
return pair{t.tr[u].x, t.tr[u].cnt}
}
mid := (t.tr[u].l + t.tr[u].r) >> 1
if r <= mid {
return t.query(u<<1, l, r)
}
if l > mid {
return t.query(u<<1|1, l, r)
}
left, right := t.query(u<<1, l, r), t.query(u<<1|1, l, r)
if left.x == right.x {
left.cnt += right.cnt
} else if left.cnt >= right.cnt {
left.cnt -= right.cnt
} else {
right.cnt -= left.cnt
left = right
}
return left
}
func (t *segmentTree) pushup(u int) {
if t.tr[u<<1].x == t.tr[u<<1|1].x {
t.tr[u].x = t.tr[u<<1].x
t.tr[u].cnt = t.tr[u<<1].cnt + t.tr[u<<1|1].cnt
} else if t.tr[u<<1].cnt >= t.tr[u<<1|1].cnt {
t.tr[u].x = t.tr[u<<1].x
t.tr[u].cnt = t.tr[u<<1].cnt - t.tr[u<<1|1].cnt
} else {
t.tr[u].x = t.tr[u<<1|1].x
t.tr[u].cnt = t.tr[u<<1|1].cnt - t.tr[u<<1].cnt
}
}
type MajorityChecker struct {
tree *segmentTree
d map[int][]int
}
func Constructor(arr []int) MajorityChecker {
tree := newSegmentTree(arr)
d := map[int][]int{}
for i, x := range arr {
d[x] = append(d[x], i)
}
return MajorityChecker{tree, d}
}
func (this *MajorityChecker) Query(left int, right int, threshold int) int {
x := this.tree.query(1, left+1, right+1).x
l := sort.SearchInts(this.d[x], left)
r := sort.SearchInts(this.d[x], right+1)
if r-l >= threshold {
return x
}
return -1
}
/**
* Your MajorityChecker object will be instantiated and called as such:
* obj := Constructor(arr);
* param_1 := obj.Query(left,right,threshold);
*/
# Accepted solution for LeetCode #1157: Online Majority Element In Subarray
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)
// Accepted solution for LeetCode #1157: Online Majority Element In Subarray
/**
* [1157] Online Majority Element In Subarray
*
* Design a data structure that efficiently finds the majority element of a given subarray.
* The majority element of a subarray is an element that occurs threshold times or more in the subarray.
* Implementing the MajorityChecker class:
*
* MajorityChecker(int[] arr) Initializes the instance of the class with the given array arr.
* int query(int left, int right, int threshold) returns the element in the subarray arr[left...right] that occurs at least threshold times, or -1 if no such element exists.
*
*
* Example 1:
*
* Input
* ["MajorityChecker", "query", "query", "query"]
* [[[1, 1, 2, 2, 1, 1]], [0, 5, 4], [0, 3, 3], [2, 3, 2]]
* Output
* [null, 1, -1, 2]
* Explanation
* MajorityChecker majorityChecker = new MajorityChecker([1, 1, 2, 2, 1, 1]);
* majorityChecker.query(0, 5, 4); // return 1
* majorityChecker.query(0, 3, 3); // return -1
* majorityChecker.query(2, 3, 2); // return 2
*
*
* Constraints:
*
* 1 <= arr.length <= 2 * 10^4
* 1 <= arr[i] <= 2 * 10^4
* 0 <= left <= right < arr.length
* threshold <= right - left + 1
* 2 * threshold > right - left + 1
* At most 10^4 calls will be made to query.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/online-majority-element-in-subarray/
// discuss: https://leetcode.com/problems/online-majority-element-in-subarray/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
// Credit: https://leetcode.com/problems/online-majority-element-in-subarray/solutions/761571/rust-translated/
struct MajorityChecker {
indices: std::collections::HashMap<i32, Vec<i32>>,
tree: Vec<Vec<i32>>,
size: i32,
}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl MajorityChecker {
fn new(arr: Vec<i32>) -> Self {
let size = arr.len();
let mut tree = vec![vec![0, 0]; size * 4];
Self::build_tree(&arr, &mut tree, 0, 0, (size - 1) as i32);
let mut indices = std::collections::HashMap::<i32, Vec<i32>>::new();
for i in 0..size {
indices.entry(arr[i]).or_default().push(i as i32);
}
Self {
tree,
indices,
size: size as i32,
}
}
fn query(&mut self, left: i32, right: i32, threshold: i32) -> i32 {
if self.size == 0 {
return -1;
}
let node = Self::find_max(&mut self.tree, 0, 0, self.size - 1, left, right);
if node[1] == 0 {
return -1;
}
let idx_arr = self.indices.get(&node[0]).unwrap();
let it1 = Self::lower_bound(&idx_arr, &left);
let it2 = Self::upper_bound(&idx_arr, &right);
let cnt = it2 - it1;
if cnt >= threshold { node[0] } else { -1 }
}
fn find_max(
tree: &mut Vec<Vec<i32>>,
root: i32,
left: i32,
right: i32,
left_limited: i32,
right_limited: i32,
) -> Vec<i32> {
let node = tree[root as usize].clone();
if left >= left_limited && right <= right_limited {
return node;
}
let mid = (left + right) / 2;
let rl = root * 2 + 1;
let rr = rl + 1;
if mid < left_limited {
return Self::find_max(tree, rr, mid + 1, right, left_limited, right_limited);
}
if mid + 1 > right_limited {
return Self::find_max(tree, rl, left, mid, left_limited, right_limited);
}
let mut t = vec![0, 0];
return Self::merge(
&mut t,
Self::find_max(tree, rl, left, mid, left_limited, right_limited),
Self::find_max(tree, rr, mid + 1, right, left_limited, right_limited),
);
}
fn build_tree(arr: &[i32], tree: &mut Vec<Vec<i32>>, root: i32, left: i32, right: i32) {
if left == right {
tree[root as usize][0] = arr[left as usize];
tree[root as usize][1] = 1;
return;
}
let mid = (left + right) / 2;
let rl = root * 2 + 1;
let rr = rl + 1;
Self::build_tree(arr, tree, rl, left, mid);
Self::build_tree(arr, tree, rr, mid + 1, right);
let l_tree = tree[rl as usize].clone();
let r_tree = tree[rr as usize].clone();
Self::merge(&mut tree[root as usize], l_tree, r_tree);
}
fn merge(root: &mut Vec<i32>, left: Vec<i32>, right: Vec<i32>) -> Vec<i32> {
if left[0] == right[0] {
root[0] = left[0];
root[1] = left[1] + right[1];
} else if left[1] > right[1] {
root[0] = left[0];
root[1] = left[1] - right[1];
} else {
root[0] = right[0];
root[1] = right[1] - left[1];
}
root.clone()
}
fn lower_bound(v: &[i32], e: &i32) -> i32 {
let mut left = 0;
let mut right = v.len();
while left < right {
let mid = left + (right - left) / 2;
if v[mid] >= *e {
right = mid;
} else {
left += 1
}
}
left as i32
}
fn upper_bound(v: &[i32], e: &i32) -> i32 {
let mut left = 0;
let mut right = v.len();
while left < right {
let mid = left + (right - left) / 2;
if v[mid] > *e {
right = mid;
} else {
left += 1
}
}
right as i32
}
}
/**
* Your MajorityChecker object will be instantiated and called as such:
* let obj = MajorityChecker::new(arr);
* let ret_1: i32 = obj.query(left, right, threshold);
*/
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1157_example_1() {
let mut majority_checker = MajorityChecker::new(vec![1, 1, 2, 2, 1, 1]);
assert_eq!(majority_checker.query(0, 5, 4), 1); // return 1
assert_eq!(majority_checker.query(0, 3, 3), -1); // return -1
assert_eq!(majority_checker.query(2, 3, 2), 2); // return 2
}
}
// Accepted solution for LeetCode #1157: Online Majority Element In Subarray
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1157: Online Majority Element In Subarray
// class Node {
// int l, r;
// int x, cnt;
// }
//
// class SegmentTree {
// private Node[] tr;
// private int[] nums;
//
// public SegmentTree(int[] nums) {
// int n = nums.length;
// this.nums = nums;
// tr = new Node[n << 2];
// for (int i = 0; i < tr.length; ++i) {
// tr[i] = new Node();
// }
// build(1, 1, n);
// }
//
// private void build(int u, int l, int r) {
// tr[u].l = l;
// tr[u].r = r;
// if (l == r) {
// tr[u].x = nums[l - 1];
// tr[u].cnt = 1;
// return;
// }
// int mid = (l + r) >> 1;
// build(u << 1, l, mid);
// build(u << 1 | 1, mid + 1, r);
// pushup(u);
// }
//
// public int[] query(int u, int l, int r) {
// if (tr[u].l >= l && tr[u].r <= r) {
// return new int[] {tr[u].x, tr[u].cnt};
// }
// int mid = (tr[u].l + tr[u].r) >> 1;
// if (r <= mid) {
// return query(u << 1, l, r);
// }
// if (l > mid) {
// return query(u << 1 | 1, l, r);
// }
// var left = query(u << 1, l, r);
// var right = query(u << 1 | 1, l, r);
// if (left[0] == right[0]) {
// left[1] += right[1];
// } else if (left[1] >= right[1]) {
// left[1] -= right[1];
// } else {
// right[1] -= left[1];
// left = right;
// }
// return left;
// }
//
// private void pushup(int u) {
// if (tr[u << 1].x == tr[u << 1 | 1].x) {
// tr[u].x = tr[u << 1].x;
// tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
// } else if (tr[u << 1].cnt >= tr[u << 1 | 1].cnt) {
// tr[u].x = tr[u << 1].x;
// tr[u].cnt = tr[u << 1].cnt - tr[u << 1 | 1].cnt;
// } else {
// tr[u].x = tr[u << 1 | 1].x;
// tr[u].cnt = tr[u << 1 | 1].cnt - tr[u << 1].cnt;
// }
// }
// }
//
// class MajorityChecker {
// private SegmentTree tree;
// private Map<Integer, List<Integer>> d = new HashMap<>();
//
// public MajorityChecker(int[] arr) {
// tree = new SegmentTree(arr);
// for (int i = 0; i < arr.length; ++i) {
// d.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
// }
// }
//
// public int query(int left, int right, int threshold) {
// int x = tree.query(1, left + 1, right + 1)[0];
// int l = search(d.get(x), left);
// int r = search(d.get(x), right + 1);
// return r - l >= threshold ? x : -1;
// }
//
// private int search(List<Integer> arr, int x) {
// int left = 0, right = arr.size();
// while (left < right) {
// int mid = (left + right) >> 1;
// if (arr.get(mid) >= x) {
// right = mid;
// } else {
// left = mid + 1;
// }
// }
// return left;
// }
// }
//
// /**
// * Your MajorityChecker object will be instantiated and called as such:
// * MajorityChecker obj = new MajorityChecker(arr);
// * int param_1 = obj.query(left,right,threshold);
// */
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.