Boundary update without `+1` / `-1`
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.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
A concert hall has n rows numbered from 0 to n - 1, each with m seats, numbered from 0 to m - 1. You need to design a ticketing system that can allocate seats in the following cases:
k spectators can sit together in a row.k spectators can get a seat. They may or may not sit together.Note that the spectators are very picky. Hence:
maxRow. maxRow can vary from group to group.Implement the BookMyShow class:
BookMyShow(int n, int m) Initializes the object with n as number of rows and m as number of seats per row.int[] gather(int k, int maxRow) Returns an array of length 2 denoting the row and seat number (respectively) of the first seat being allocated to the k members of the group, who must sit together. In other words, it returns the smallest possible r and c such that all [c, c + k - 1] seats are valid and empty in row r, and r <= maxRow. Returns [] in case it is not possible to allocate seats to the group.boolean scatter(int k, int maxRow) Returns true if all k members of the group can be allocated seats in rows 0 to maxRow, who may or may not sit together. If the seats can be allocated, it allocates k seats to the group with the smallest row numbers, and the smallest possible seat numbers in each row. Otherwise, returns false.Example 1:
Input
["BookMyShow", "gather", "gather", "scatter", "scatter"]
[[2, 5], [4, 0], [2, 0], [5, 1], [5, 1]]
Output
[null, [0, 0], [], true, false]
Explanation
BookMyShow bms = new BookMyShow(2, 5); // There are 2 rows with 5 seats each
bms.gather(4, 0); // return [0, 0]
// The group books seats [0, 3] of row 0.
bms.gather(2, 0); // return []
// There is only 1 seat left in row 0,
// so it is not possible to book 2 consecutive seats.
bms.scatter(5, 1); // return True
// The group books seat 4 of row 0 and seats [0, 3] of row 1.
bms.scatter(5, 1); // return False
// There is only one seat left in the hall.
Constraints:
1 <= n <= 5 * 1041 <= m, k <= 1090 <= maxRow <= n - 15 * 104 calls in total will be made to gather and scatter.Problem summary: A concert hall has n rows numbered from 0 to n - 1, each with m seats, numbered from 0 to m - 1. You need to design a ticketing system that can allocate seats in the following cases: If a group of k spectators can sit together in a row. If every member of a group of k spectators can get a seat. They may or may not sit together. Note that the spectators are very picky. Hence: They will book seats only if each member of their group can get a seat with row number less than or equal to maxRow. maxRow can vary from group to group. In case there are multiple rows to choose from, the row with the smallest number is chosen. If there are multiple seats to choose in the same row, the seat with the smallest number is chosen. Implement the BookMyShow class: BookMyShow(int n, int m) Initializes the object with n as number of rows and m as number of seats per row. int[] gather(int k, int maxRow)
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Binary Search · Design · Segment Tree
["BookMyShow","gather","gather","scatter","scatter"] [[2,5],[4,0],[2,0],[5,1],[5,1]]
cinema-seat-allocation)longest-increasing-subsequence-ii)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2286: Booking Concert Tickets in Groups
class Node {
int l, r;
long mx, s;
}
class SegmentTree {
private Node[] tr;
private int m;
public SegmentTree(int n, int m) {
this.m = m;
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].s = m;
tr[u].mx = m;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
public void modify(int u, int x, long v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].s = v;
tr[u].mx = v;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) {
modify(u << 1, x, v);
} else {
modify(u << 1 | 1, x, v);
}
pushup(u);
}
public long querySum(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].s;
}
int mid = (tr[u].l + tr[u].r) >> 1;
long v = 0;
if (l <= mid) {
v += querySum(u << 1, l, r);
}
if (r > mid) {
v += querySum(u << 1 | 1, l, r);
}
return v;
}
public int queryIdx(int u, int l, int r, int k) {
if (tr[u].mx < k) {
return 0;
}
if (tr[u].l == tr[u].r) {
return tr[u].l;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (tr[u << 1].mx >= k) {
return queryIdx(u << 1, l, r, k);
}
if (r > mid) {
return queryIdx(u << 1 | 1, l, r, k);
}
return 0;
}
private void pushup(int u) {
tr[u].s = tr[u << 1].s + tr[u << 1 | 1].s;
tr[u].mx = Math.max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}
}
class BookMyShow {
private int n;
private int m;
private SegmentTree tree;
public BookMyShow(int n, int m) {
this.n = n;
this.m = m;
tree = new SegmentTree(n, m);
}
public int[] gather(int k, int maxRow) {
++maxRow;
int i = tree.queryIdx(1, 1, maxRow, k);
if (i == 0) {
return new int[] {};
}
long s = tree.querySum(1, i, i);
tree.modify(1, i, s - k);
return new int[] {i - 1, (int) (m - s)};
}
public boolean scatter(int k, int maxRow) {
++maxRow;
if (tree.querySum(1, 1, maxRow) < k) {
return false;
}
int i = tree.queryIdx(1, 1, maxRow, 1);
for (int j = i; j <= n; ++j) {
long s = tree.querySum(1, j, j);
if (s >= k) {
tree.modify(1, j, s - k);
return true;
}
k -= s;
tree.modify(1, j, 0);
}
return true;
}
}
/**
* Your BookMyShow object will be instantiated and called as such:
* BookMyShow obj = new BookMyShow(n, m);
* int[] param_1 = obj.gather(k,maxRow);
* boolean param_2 = obj.scatter(k,maxRow);
*/
// Accepted solution for LeetCode #2286: Booking Concert Tickets in Groups
type BookMyShow struct {
n, m int
tree *segmentTree
}
func Constructor(n int, m int) BookMyShow {
return BookMyShow{n, m, newSegmentTree(n, m)}
}
func (this *BookMyShow) Gather(k int, maxRow int) []int {
maxRow++
i := this.tree.queryIdx(1, 1, maxRow, k)
if i == 0 {
return []int{}
}
s := this.tree.querySum(1, i, i)
this.tree.modify(1, i, s-k)
return []int{i - 1, this.m - s}
}
func (this *BookMyShow) Scatter(k int, maxRow int) bool {
maxRow++
if this.tree.querySum(1, 1, maxRow) < k {
return false
}
i := this.tree.queryIdx(1, 1, maxRow, 1)
for j := i; j <= this.n; j++ {
s := this.tree.querySum(1, j, j)
if s >= k {
this.tree.modify(1, j, s-k)
return true
}
k -= s
this.tree.modify(1, j, 0)
}
return true
}
type node struct {
l, r, s, mx int
}
type segmentTree struct {
tr []*node
m int
}
func newSegmentTree(n, m int) *segmentTree {
tr := make([]*node, n<<2)
for i := range tr {
tr[i] = &node{}
}
t := &segmentTree{tr, m}
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].s, t.tr[u].mx = t.m, t.m
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) modify(u, x, v int) {
if t.tr[u].l == x && t.tr[u].r == x {
t.tr[u].s, t.tr[u].mx = v, v
return
}
mid := (t.tr[u].l + t.tr[u].r) >> 1
if x <= mid {
t.modify(u<<1, x, v)
} else {
t.modify(u<<1|1, x, v)
}
t.pushup(u)
}
func (t *segmentTree) querySum(u, l, r int) int {
if t.tr[u].l >= l && t.tr[u].r <= r {
return t.tr[u].s
}
mid := (t.tr[u].l + t.tr[u].r) >> 1
v := 0
if l <= mid {
v = t.querySum(u<<1, l, r)
}
if r > mid {
v += t.querySum(u<<1|1, l, r)
}
return v
}
func (t *segmentTree) queryIdx(u, l, r, k int) int {
if t.tr[u].mx < k {
return 0
}
if t.tr[u].l == t.tr[u].r {
return t.tr[u].l
}
mid := (t.tr[u].l + t.tr[u].r) >> 1
if t.tr[u<<1].mx >= k {
return t.queryIdx(u<<1, l, r, k)
}
if r > mid {
return t.queryIdx(u<<1|1, l, r, k)
}
return 0
}
func (t *segmentTree) pushup(u int) {
t.tr[u].s = t.tr[u<<1].s + t.tr[u<<1|1].s
t.tr[u].mx = max(t.tr[u<<1].mx, t.tr[u<<1|1].mx)
}
/**
* Your BookMyShow object will be instantiated and called as such:
* obj := Constructor(n, m);
* param_1 := obj.Gather(k,maxRow);
* param_2 := obj.Scatter(k,maxRow);
*/
# Accepted solution for LeetCode #2286: Booking Concert Tickets in Groups
class Node:
__slots__ = "l", "r", "s", "mx"
def __init__(self):
self.l = self.r = 0
self.s = self.mx = 0
class SegmentTree:
def __init__(self, n, m):
self.m = m
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].s = self.tr[u].mx = self.m
return
mid = (l + r) >> 1
self.build(u << 1, l, mid)
self.build(u << 1 | 1, mid + 1, r)
self.pushup(u)
def modify(self, u, x, v):
if self.tr[u].l == x and self.tr[u].r == x:
self.tr[u].s = self.tr[u].mx = v
return
mid = (self.tr[u].l + self.tr[u].r) >> 1
if x <= mid:
self.modify(u << 1, x, v)
else:
self.modify(u << 1 | 1, x, v)
self.pushup(u)
def query_sum(self, u, l, r):
if self.tr[u].l >= l and self.tr[u].r <= r:
return self.tr[u].s
mid = (self.tr[u].l + self.tr[u].r) >> 1
v = 0
if l <= mid:
v += self.query_sum(u << 1, l, r)
if r > mid:
v += self.query_sum(u << 1 | 1, l, r)
return v
def query_idx(self, u, l, r, k):
if self.tr[u].mx < k:
return 0
if self.tr[u].l == self.tr[u].r:
return self.tr[u].l
mid = (self.tr[u].l + self.tr[u].r) >> 1
if self.tr[u << 1].mx >= k:
return self.query_idx(u << 1, l, r, k)
if r > mid:
return self.query_idx(u << 1 | 1, l, r, k)
return 0
def pushup(self, u):
self.tr[u].s = self.tr[u << 1].s + self.tr[u << 1 | 1].s
self.tr[u].mx = max(self.tr[u << 1].mx, self.tr[u << 1 | 1].mx)
class BookMyShow:
def __init__(self, n: int, m: int):
self.n = n
self.tree = SegmentTree(n, m)
def gather(self, k: int, maxRow: int) -> List[int]:
maxRow += 1
i = self.tree.query_idx(1, 1, maxRow, k)
if i == 0:
return []
s = self.tree.query_sum(1, i, i)
self.tree.modify(1, i, s - k)
return [i - 1, self.tree.m - s]
def scatter(self, k: int, maxRow: int) -> bool:
maxRow += 1
if self.tree.query_sum(1, 1, maxRow) < k:
return False
i = self.tree.query_idx(1, 1, maxRow, 1)
for j in range(i, self.n + 1):
s = self.tree.query_sum(1, j, j)
if s >= k:
self.tree.modify(1, j, s - k)
return True
k -= s
self.tree.modify(1, j, 0)
return True
# Your BookMyShow object will be instantiated and called as such:
# obj = BookMyShow(n, m)
# param_1 = obj.gather(k,maxRow)
# param_2 = obj.scatter(k,maxRow)
// Accepted solution for LeetCode #2286: Booking Concert Tickets in Groups
/**
* [2286] Booking Concert Tickets in Groups
*
* A concert hall has n rows numbered from 0 to n - 1, each with m seats, numbered from 0 to m - 1. You need to design a ticketing system that can allocate seats in the following cases:
*
* If a group of k spectators can sit together in a row.
* If every member of a group of k spectators can get a seat. They may or may not sit together.
*
* Note that the spectators are very picky. Hence:
*
* They will book seats only if each member of their group can get a seat with row number less than or equal to maxRow. maxRow can vary from group to group.
* In case there are multiple rows to choose from, the row with the smallest number is chosen. If there are multiple seats to choose in the same row, the seat with the smallest number is chosen.
*
* Implement the BookMyShow class:
*
* BookMyShow(int n, int m) Initializes the object with n as number of rows and m as number of seats per row.
* int[] gather(int k, int maxRow) Returns an array of length 2 denoting the row and seat number (respectively) of the first seat being allocated to the k members of the group, who must sit together. In other words, it returns the smallest possible r and c such that all [c, c + k - 1] seats are valid and empty in row r, and r <= maxRow. Returns [] in case it is not possible to allocate seats to the group.
* boolean scatter(int k, int maxRow) Returns true if all k members of the group can be allocated seats in rows 0 to maxRow, who may or may not sit together. If the seats can be allocated, it allocates k seats to the group with the smallest row numbers, and the smallest possible seat numbers in each row. Otherwise, returns false.
*
*
* Example 1:
*
* Input
* ["BookMyShow", "gather", "gather", "scatter", "scatter"]
* [[2, 5], [4, 0], [2, 0], [5, 1], [5, 1]]
* Output
* [null, [0, 0], [], true, false]
* Explanation
* BookMyShow bms = new BookMyShow(2, 5); // There are 2 rows with 5 seats each
* bms.gather(4, 0); // return [0, 0]
* // The group books seats [0, 3] of row 0.
* bms.gather(2, 0); // return []
* // There is only 1 seat left in row 0,
* // so it is not possible to book 2 consecutive seats.
* bms.scatter(5, 1); // return True
* // The group books seat 4 of row 0 and seats [0, 3] of row 1.
* bms.scatter(5, 1); // return False
* // There is only one seat left in the hall.
*
*
* Constraints:
*
* 1 <= n <= 5 * 10^4
* 1 <= m, k <= 10^9
* 0 <= maxRow <= n - 1
* At most 5 * 10^4 calls in total will be made to gather and scatter.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/booking-concert-tickets-in-groups/
// discuss: https://leetcode.com/problems/booking-concert-tickets-in-groups/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
struct BookMyShow {}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl BookMyShow {
fn new(n: i32, m: i32) -> Self {
Self {}
}
fn gather(&self, k: i32, max_row: i32) -> Vec<i32> {
vec![]
}
fn scatter(&self, k: i32, max_row: i32) -> bool {
false
}
}
/**
* Your BookMyShow object will be instantiated and called as such:
* let obj = BookMyShow::new(n, m);
* let ret_1: Vec<i32> = obj.gather(k, maxRow);
* let ret_2: bool = obj.scatter(k, maxRow);
*/
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_2286_example_1() {
let mut bms = BookMyShow::new(2, 5); // There are 2 rows with 5 seats each
assert_eq!(bms.gather(4, 0), vec![0, 0]); // return [0, 0]
// The group books seats [0, 3] of row 0.
assert_eq!(bms.gather(2, 0), vec![]); // return []
// There is only 1 seat left in row 0,
// so it is not possible to book 2 consecutive seats.
assert_eq!(bms.scatter(5, 1), true); // return True
// The group books seat 4 of row 0 and seats [0, 3] of row 1.
assert_eq!(bms.scatter(5, 1), false); // return False
// There is only one seat left in the hall.
}
}
// Accepted solution for LeetCode #2286: Booking Concert Tickets in Groups
class Node {
l: number;
r: number;
mx: number;
s: number;
constructor() {
this.l = 0;
this.r = 0;
this.mx = 0;
this.s = 0;
}
}
class SegmentTree {
private tr: Node[];
private m: number;
constructor(n: number, m: number) {
this.m = m;
this.tr = Array.from({ length: n << 2 }, () => new Node());
this.build(1, 1, n);
}
private build(u: number, l: number, r: number): void {
this.tr[u].l = l;
this.tr[u].r = r;
if (l === r) {
this.tr[u].s = this.m;
this.tr[u].mx = this.m;
return;
}
const mid = (l + r) >> 1;
this.build(u << 1, l, mid);
this.build((u << 1) | 1, mid + 1, r);
this.pushup(u);
}
public modify(u: number, x: number, v: number): void {
if (this.tr[u].l === x && this.tr[u].r === x) {
this.tr[u].s = v;
this.tr[u].mx = v;
return;
}
const mid = (this.tr[u].l + this.tr[u].r) >> 1;
if (x <= mid) {
this.modify(u << 1, x, v);
} else {
this.modify((u << 1) | 1, x, v);
}
this.pushup(u);
}
public querySum(u: number, l: number, r: number): number {
if (this.tr[u].l >= l && this.tr[u].r <= r) {
return this.tr[u].s;
}
const mid = (this.tr[u].l + this.tr[u].r) >> 1;
let v = 0;
if (l <= mid) {
v += this.querySum(u << 1, l, r);
}
if (r > mid) {
v += this.querySum((u << 1) | 1, l, r);
}
return v;
}
public queryIdx(u: number, l: number, r: number, k: number): number {
if (this.tr[u].mx < k) {
return 0;
}
if (this.tr[u].l === this.tr[u].r) {
return this.tr[u].l;
}
const mid = (this.tr[u].l + this.tr[u].r) >> 1;
if (this.tr[u << 1].mx >= k) {
return this.queryIdx(u << 1, l, r, k);
}
if (r > mid) {
return this.queryIdx((u << 1) | 1, l, r, k);
}
return 0;
}
private pushup(u: number): void {
this.tr[u].s = this.tr[u << 1].s + this.tr[(u << 1) | 1].s;
this.tr[u].mx = Math.max(this.tr[u << 1].mx, this.tr[(u << 1) | 1].mx);
}
}
class BookMyShow {
private n: number;
private m: number;
private tree: SegmentTree;
constructor(n: number, m: number) {
this.n = n;
this.m = m;
this.tree = new SegmentTree(n, m);
}
public gather(k: number, maxRow: number): number[] {
++maxRow;
const i = this.tree.queryIdx(1, 1, maxRow, k);
if (i === 0) {
return [];
}
const s = this.tree.querySum(1, i, i);
this.tree.modify(1, i, s - k);
return [i - 1, this.m - s];
}
public scatter(k: number, maxRow: number): boolean {
++maxRow;
if (this.tree.querySum(1, 1, maxRow) < k) {
return false;
}
let i = this.tree.queryIdx(1, 1, maxRow, 1);
for (let j = i; j <= this.n; ++j) {
const s = this.tree.querySum(1, j, j);
if (s >= k) {
this.tree.modify(1, j, s - k);
return true;
}
k -= s;
this.tree.modify(1, j, 0);
}
return true;
}
}
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: 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.