Segment Tree
Last updated: Apr 3, 2026Table of Contents
- Overview
- Key Properties
- Core Characteristics
- Problem Categories
- Category 1: Range Sum Queries
- Category 2: Range Minimum/Maximum Queries
- Category 3: Range Updates with Lazy Propagation
- Category 4: Order Statistics & Inversions
- Data Structure Comparison
- BIT vs Segment Tree Comparison
- Templates & Algorithms
- Template 1: Binary Indexed Tree (Fenwick Tree)
- Template 2: Segment Tree (Range Sum)
- Template 3: Segment Tree with Lazy Propagation
- Template 4: 2D Binary Indexed Tree
- LeetCode Problems & Solutions
- Range Sum Query Problems
- Order Statistics Problems
- LC 307: Range Sum Query - Mutable
- LC 315: Count of Smaller Numbers After Self
- LC 493: Reverse Pairs
- LC 327: Count of Range Sum
- Advanced Techniques
- Coordinate Compression
- Range Maximum Query (RMQ) Segment Tree
- Performance Analysis
- Time Complexity Comparison
- Space Complexity
- Implementation Tips
- Common Pitfalls & Solutions
- Summary & Quick Reference
- When to Use Each Structure
- Implementation Checklist
- LeetCode Problem Categories
- LC Examples
- 2-1) Range Sum Query - Mutable (LC 307) — Segment Tree
- 2-2) My Calendar I (LC 729) — Segment Tree / TreeMap for Interval Booking
- 2-3) Segment Tree Template — Range Sum with Lazy Propagation
- 2-4) Corporate Flight Bookings (LC 1109) — Difference Array
- 2-5) Count of Smaller Numbers After Self (LC 315) — Iterative Segment Tree
- 2-6) Falling Squares (LC 699) — Segment Tree Max
- 2-7) Maximum Sum Rectangle No Larger Than K (LC 363) — Prefix Sum + TreeSet
- 2-8) Range Module (LC 715) — Segment Tree / TreeMap
- 2-9) Longest Increasing Subsequence (LC 300) — Segment Tree on Values
- 2-10) My Calendar II (LC 731) — Segment Tree / TreeMap
Segment Tree & Binary Indexed Tree (Fenwick Tree)
Overview
Segment Trees and Binary Indexed Trees (BIT/Fenwick Tree) are advanced data structures for efficiently handling range queries and updates on arrays.
Key Properties
- Time Complexity: O(log n) for both query and update operations
- Space Complexity: O(n) for BIT, O(4n) for Segment Tree
- Core Idea: Precompute range information in tree structure for fast queries
- When to Use: Range sum/min/max queries with updates, order statistics
- Key Operations: Range query, point/range update, build tree
Core Characteristics
- Range Queries: Sum, minimum, maximum, GCD, XOR over ranges
- Point Updates: Modify single element efficiently
- Range Updates: Modify entire ranges (with lazy propagation)
- Space-Time Tradeoff: Extra space for faster query processing
Problem Categories
Category 1: Range Sum Queries
- Description: Calculate sum over ranges with updates
- Examples: LC 307 (Range Sum Query - Mutable), LC 308 (Range Sum Query 2D - Mutable)
- Pattern: Use BIT or Segment Tree for point updates, range queries
Category 2: Range Minimum/Maximum Queries
- Description: Find min/max in ranges with updates
- Examples: LC 315 (Count of Smaller Numbers After Self), Custom RMQ problems
- Pattern: Segment Tree with min/max operations
Category 3: Range Updates with Lazy Propagation
- Description: Update entire ranges efficiently
- Examples: Add value to range, set range to value
- Pattern: Segment Tree with lazy propagation
Category 4: Order Statistics & Inversions
- Description: Count smaller/larger elements, inversions
- Examples: LC 315 (Count Smaller), LC 493 (Reverse Pairs), LC 327 (Count Range Sum)
- Pattern: BIT with coordinate compression or merge sort
Data Structure Comparison
BIT vs Segment Tree Comparison
| Aspect | Binary Indexed Tree | Segment Tree |
|---|---|---|
| Space | O(n) | O(4n) |
| Implementation | Simple, short code | More complex |
| Operations | Sum, XOR, OR | Any associative operation |
| Range Updates | Difficult | Easy with lazy propagation |
| 1-indexed | Natural fit | Can be adapted |
| Query Types | Prefix queries easy | Arbitrary range queries |
Templates & Algorithms
Template 1: Binary Indexed Tree (Fenwick Tree)
class BIT:
"""Binary Indexed Tree for range sum queries and point updates"""
def __init__(self, n):
self.n = n
self.tree = [0] * (n + 1) # 1-indexed
def update(self, i, delta):
"""Add delta to element at index i"""
while i <= self.n:
self.tree[i] += delta
i += i & (-i) # Add lowest set bit
def query(self, i):
"""Get prefix sum from 1 to i"""
total = 0
while i > 0:
total += self.tree[i]
i -= i & (-i) # Remove lowest set bit
return total
def range_query(self, left, right):
"""Get sum from left to right (inclusive)"""
if left > 1:
return self.query(right) - self.query(left - 1)
else:
return self.query(right)
def build(self, arr):
"""Build BIT from array (1-indexed)"""
for i in range(1, len(arr)):
self.update(i, arr[i])
Template 2: Segment Tree (Range Sum)
class SegmentTree:
"""Segment Tree for range sum queries and point updates"""
def __init__(self, arr):
self.n = len(arr)
self.tree = [0] * (4 * self.n) # 4x space for safety
self.build(arr, 1, 0, self.n - 1)
def build(self, arr, node, start, end):
"""Build segment tree recursively"""
if start == end:
self.tree[node] = arr[start]
else:
mid = (start + end) // 2
self.build(arr, 2 * node, start, mid)
self.build(arr, 2 * node + 1, mid + 1, end)
self.tree[node] = self.tree[2 * node] + self.tree[2 * node + 1]
def update(self, node, start, end, idx, val):
"""Update single element at index idx to val"""
if start == end:
self.tree[node] = val
else:
mid = (start + end) // 2
if idx <= mid:
self.update(2 * node, start, mid, idx, val)
else:
self.update(2 * node + 1, mid + 1, end, idx, val)
self.tree[node] = self.tree[2 * node] + self.tree[2 * node + 1]
def query(self, node, start, end, left, right):
"""Query sum in range [left, right]"""
if right < start or end < left:
return 0 # No overlap
if left <= start and end <= right:
return self.tree[node] # Complete overlap
# Partial overlap
mid = (start + end) // 2
left_sum = self.query(2 * node, start, mid, left, right)
right_sum = self.query(2 * node + 1, mid + 1, end, left, right)
return left_sum + right_sum
# Public interface methods
def point_update(self, idx, val):
"""Update element at index idx to val"""
self.update(1, 0, self.n - 1, idx, val)
def range_sum(self, left, right):
"""Get sum in range [left, right]"""
return self.query(1, 0, self.n - 1, left, right)
Template 3: Segment Tree with Lazy Propagation
class LazySegmentTree:
"""Segment Tree with lazy propagation for range updates"""
def __init__(self, arr):
self.n = len(arr)
self.tree = [0] * (4 * self.n)
self.lazy = [0] * (4 * self.n)
self.build(arr, 1, 0, self.n - 1)
def build(self, arr, node, start, end):
if start == end:
self.tree[node] = arr[start]
else:
mid = (start + end) // 2
self.build(arr, 2 * node, start, mid)
self.build(arr, 2 * node + 1, mid + 1, end)
self.tree[node] = self.tree[2 * node] + self.tree[2 * node + 1]
def push(self, node, start, end):
"""Push lazy value down to children"""
if self.lazy[node] != 0:
self.tree[node] += self.lazy[node] * (end - start + 1)
if start != end: # Not a leaf node
self.lazy[2 * node] += self.lazy[node]
self.lazy[2 * node + 1] += self.lazy[node]
self.lazy[node] = 0
def update_range(self, node, start, end, left, right, val):
"""Add val to range [left, right]"""
self.push(node, start, end)
if start > right or end < left:
return
if start >= left and end <= right:
self.lazy[node] += val
self.push(node, start, end)
return
mid = (start + end) // 2
self.update_range(2 * node, start, mid, left, right, val)
self.update_range(2 * node + 1, mid + 1, end, left, right, val)
self.push(2 * node, start, mid)
self.push(2 * node + 1, mid + 1, end)
self.tree[node] = self.tree[2 * node] + self.tree[2 * node + 1]
def query_range(self, node, start, end, left, right):
"""Query sum in range [left, right]"""
if start > right or end < left:
return 0
self.push(node, start, end)
if start >= left and end <= right:
return self.tree[node]
mid = (start + end) // 2
left_sum = self.query_range(2 * node, start, mid, left, right)
right_sum = self.query_range(2 * node + 1, mid + 1, end, left, right)
return left_sum + right_sum
# Public interface
def range_add(self, left, right, val):
"""Add val to range [left, right]"""
self.update_range(1, 0, self.n - 1, left, right, val)
def range_sum(self, left, right):
"""Get sum in range [left, right]"""
return self.query_range(1, 0, self.n - 1, left, right)
Template 4: 2D Binary Indexed Tree
class BIT2D:
"""2D Binary Indexed Tree for 2D range sum queries"""
def __init__(self, rows, cols):
self.rows = rows
self.cols = cols
self.tree = [[0] * (cols + 1) for _ in range(rows + 1)]
def update(self, row, col, delta):
"""Add delta to element at (row, col)"""
orig_col = col
while row <= self.rows:
col = orig_col
while col <= self.cols:
self.tree[row][col] += delta
col += col & (-col)
row += row & (-row)
def query(self, row, col):
"""Get sum from (1,1) to (row, col)"""
total = 0
orig_col = col
while row > 0:
col = orig_col
while col > 0:
total += self.tree[row][col]
col -= col & (-col)
row -= row & (-row)
return total
def range_query(self, row1, col1, row2, col2):
"""Get sum in rectangle from (row1, col1) to (row2, col2)"""
return (self.query(row2, col2) -
self.query(row1 - 1, col2) -
self.query(row2, col1 - 1) +
self.query(row1 - 1, col1 - 1))
LeetCode Problems & Solutions
Range Sum Query Problems
| Problem | LC # | Data Structure | Difficulty | Key Technique |
|---|---|---|---|---|
| Range Sum Query - Immutable | 303 | Prefix Sum | Easy | Simple prefix array |
| Range Sum Query - Mutable | 307 | BIT/Segment Tree | Medium | Point update, range query |
| Range Sum Query 2D - Immutable | 304 | 2D Prefix Sum | Medium | 2D prefix array |
| Range Sum Query 2D - Mutable | 308 | 2D BIT | Hard | 2D point update, range query |
Order Statistics Problems
| Problem | LC # | Data Structure | Difficulty | Key Technique |
|---|---|---|---|---|
| Count of Smaller Numbers After Self | 315 | BIT + Compression | Hard | Coordinate compression |
| Reverse Pairs | 493 | BIT/Merge Sort | Hard | Count inversions |
| Count of Range Sum | 327 | BIT + Prefix Sum | Hard | Coordinate compression |
LC 307: Range Sum Query - Mutable
class NumArray:
"""Range Sum Query with updates using BIT"""
def __init__(self, nums):
self.nums = [0] + nums # Make 1-indexed
self.bit = BIT(len(nums))
# Build BIT
for i in range(1, len(self.nums)):
self.bit.update(i, self.nums[i])
def update(self, index, val):
"""Update element at index to val"""
index += 1 # Convert to 1-indexed
delta = val - self.nums[index]
self.nums[index] = val
self.bit.update(index, delta)
def sumRange(self, left, right):
"""Sum elements from left to right"""
return self.bit.range_query(left + 1, right + 1)
# Alternative using Segment Tree
class NumArraySegTree:
def __init__(self, nums):
self.seg_tree = SegmentTree(nums)
self.nums = nums
def update(self, index, val):
self.nums[index] = val
self.seg_tree.point_update(index, val)
def sumRange(self, left, right):
return self.seg_tree.range_sum(left, right)
LC 315: Count of Smaller Numbers After Self
def countSmaller(nums):
"""Count smaller numbers after self using BIT"""
if not nums:
return []
# Coordinate compression
sorted_nums = sorted(set(nums))
rank = {num: i + 1 for i, num in enumerate(sorted_nums)}
bit = BIT(len(sorted_nums))
result = []
# Process from right to left
for i in range(len(nums) - 1, -1, -1):
# Count numbers smaller than nums[i]
count = bit.query(rank[nums[i]] - 1) if rank[nums[i]] > 1 else 0
result.append(count)
# Add current number to BIT
bit.update(rank[nums[i]], 1)
return result[::-1] # Reverse to get correct order
# Alternative using merge sort
def countSmallerMergeSort(nums):
"""Using merge sort to count inversions"""
def mergeSort(arr):
if len(arr) <= 1:
return arr, [0] * len(arr)
mid = len(arr) // 2
left, left_counts = mergeSort(arr[:mid])
right, right_counts = mergeSort(arr[mid:])
merged = []
counts = [0] * len(arr)
i = j = 0
while i < len(left) and j < len(right):
if left[i][0] <= right[j][0]:
merged.append(left[i])
counts[left[i][1]] += j # j elements from right are smaller
i += 1
else:
merged.append(right[j])
j += 1
while i < len(left):
merged.append(left[i])
counts[left[i][1]] += j
i += 1
while j < len(right):
merged.append(right[j])
j += 1
return merged, counts
# Create (value, original_index) pairs
indexed_nums = [(nums[i], i) for i in range(len(nums))]
_, counts = mergeSort(indexed_nums)
return counts
LC 493: Reverse Pairs
def reversePairs(nums):
"""Count reverse pairs using BIT and coordinate compression"""
if not nums:
return 0
# Get all possible values (including doubled values)
values = set(nums)
for num in nums:
values.add(2 * num)
# Coordinate compression
sorted_values = sorted(values)
rank = {val: i + 1 for i, val in enumerate(sorted_values)}
bit = BIT(len(sorted_values))
count = 0
for num in reversed(nums):
# Count how many numbers > 2 * num are already seen
target_rank = rank[2 * num]
# Query from target_rank+1 to end
if target_rank < len(sorted_values):
count += bit.query(len(sorted_values)) - bit.query(target_rank)
# Add current number to BIT
bit.update(rank[num], 1)
return count
# Alternative merge sort approach
def reversePairsMergeSort(nums):
def mergeSort(arr, start, end):
if start >= end:
return 0
mid = (start + end) // 2
count = mergeSort(arr, start, mid) + mergeSort(arr, mid + 1, end)
# Count reverse pairs
j = mid + 1
for i in range(start, mid + 1):
while j <= end and arr[i] > 2 * arr[j]:
j += 1
count += j - (mid + 1)
# Merge sorted arrays
arr[start:end + 1] = sorted(arr[start:end + 1])
return count
return mergeSort(nums, 0, len(nums) - 1)
LC 327: Count of Range Sum
def countRangeSum(nums, lower, upper):
"""Count range sums in [lower, upper] using BIT"""
if not nums:
return 0
# Compute prefix sums
prefix_sums = [0]
for num in nums:
prefix_sums.append(prefix_sums[-1] + num)
# Get all relevant values for coordinate compression
values = set(prefix_sums)
for ps in prefix_sums:
values.add(ps - lower)
values.add(ps - upper)
sorted_values = sorted(values)
rank = {val: i + 1 for i, val in enumerate(sorted_values)}
bit = BIT(len(sorted_values))
count = 0
for ps in prefix_sums:
# Count prefix sums in range [ps - upper, ps - lower]
left_rank = rank[ps - upper]
right_rank = rank[ps - lower]
count += bit.range_query(left_rank, right_rank)
# Add current prefix sum to BIT
bit.update(rank[ps], 1)
return count
Advanced Techniques
Coordinate Compression
def coordinate_compress(arr):
"""Compress coordinates for BIT usage"""
unique_vals = sorted(set(arr))
rank_map = {val: i + 1 for i, val in enumerate(unique_vals)}
return rank_map, len(unique_vals)
def use_compression_example():
nums = [100, 1, 50, 200, 75]
rank_map, max_rank = coordinate_compress(nums)
# rank_map = {1: 1, 50: 2, 75: 3, 100: 4, 200: 5}
bit = BIT(max_rank)
for num in nums:
bit.update(rank_map[num], 1) # Add frequency
Range Maximum Query (RMQ) Segment Tree
class RMQSegmentTree:
"""Segment Tree for Range Maximum Queries"""
def __init__(self, arr):
self.n = len(arr)
self.tree = [0] * (4 * self.n)
self.build(arr, 1, 0, self.n - 1)
def build(self, arr, node, start, end):
if start == end:
self.tree[node] = arr[start]
else:
mid = (start + end) // 2
self.build(arr, 2 * node, start, mid)
self.build(arr, 2 * node + 1, mid + 1, end)
self.tree[node] = max(self.tree[2 * node], self.tree[2 * node + 1])
def query_max(self, node, start, end, left, right):
if right < start or end < left:
return float('-inf')
if left <= start and end <= right:
return self.tree[node]
mid = (start + end) // 2
left_max = self.query_max(2 * node, start, mid, left, right)
right_max = self.query_max(2 * node + 1, mid + 1, end, left, right)
return max(left_max, right_max)
def range_max(self, left, right):
return self.query_max(1, 0, self.n - 1, left, right)
Performance Analysis
Time Complexity Comparison
| Operation | Naive Array | BIT | Segment Tree | Sparse Table |
|---|---|---|---|---|
| Build | O(1) | O(n log n) | O(n) | O(n log n) |
| Point Update | O(1) | O(log n) | O(log n) | O(n) |
| Range Query | O(n) | O(log n) | O(log n) | O(1) |
| Range Update | O(n) | O(log n) | O(log n) | O(n) |
Space Complexity
- BIT: O(n) - very space efficient
- Segment Tree: O(4n) - needs more space but more flexible
- 2D BIT: O(n×m) - scales quadratically
- Lazy Segment Tree: O(4n) - same as regular segment tree
Implementation Tips
Common Pitfalls & Solutions
def bit_pitfalls():
"""Common BIT implementation mistakes"""
# ❌ Wrong: 0-indexed BIT
# BIT naturally works with 1-indexed arrays
# ✅ Correct: Convert to 1-indexed
def update_correct(bit, index, delta):
index += 1 # Convert 0-indexed to 1-indexed
while index <= bit.n:
bit.tree[index] += delta
index += index & (-index)
# ❌ Wrong: Forgetting coordination compression
def wrong_approach(nums):
bit = BIT(max(nums)) # Might use too much memory
# ✅ Correct: Use coordinate compression
def correct_approach(nums):
rank_map, size = coordinate_compress(nums)
bit = BIT(size)
for num in nums:
bit.update(rank_map[num], 1)
def segment_tree_tips():
"""Segment Tree best practices"""
# Use 4n space allocation for safety
tree = [0] * (4 * n)
# Handle edge cases properly
def query(node, start, end, left, right):
if right < start or end < left:
return 0 # Return identity element
# ... rest of query logic
Summary & Quick Reference
When to Use Each Structure
| Use Case | Best Choice | Why |
|---|---|---|
| Range Sum + Point Updates | BIT | Simple, space-efficient |
| Range Min/Max + Updates | Segment Tree | Supports any associative operation |
| Range Updates | Lazy Segment Tree | Efficient batch updates |
| 2D Range Queries | 2D BIT | Natural extension |
| Count Inversions | BIT + Compression | Perfect for order statistics |
Implementation Checklist
- [ ] BIT: Remember 1-indexing, use coordinate compression for large values
- [ ] Segment Tree: Allocate 4n space, handle query edge cases
- [ ] Lazy Propagation: Implement push correctly, update children lazily
- [ ] 2D Structures: Consider memory usage, test with small examples first
LeetCode Problem Categories
- Range Sum: LC 303, 307, 308 (BIT/Segment Tree)
- Order Statistics: LC 315, 327, 493 (BIT + Compression)
- Dynamic Programming: Range DP with RMQ optimization
- Geometry: 2D range queries, rectangle problems
LC Examples
2-1) Range Sum Query - Mutable (LC 307) — Segment Tree
Segment tree supports O(log N) range sum query and point update.
// LC 307 - Range Sum Query - Mutable
// IDEA: Segment Tree — build, update, query in O(log N)
// time = O(log N) per op, space = O(N)
class NumArray {
int[] tree;
int n;
public NumArray(int[] nums) {
n = nums.length;
tree = new int[2 * n];
// build leaves
for (int i = 0; i < n; i++) tree[n + i] = nums[i];
// build internal nodes
for (int i = n - 1; i >= 1; i--) tree[i] = tree[2*i] + tree[2*i+1];
}
public void update(int i, int val) {
tree[n + i] = val;
for (int pos = (n + i) >> 1; pos >= 1; pos >>= 1)
tree[pos] = tree[2*pos] + tree[2*pos+1];
}
public int sumRange(int l, int r) {
int sum = 0;
for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) {
if ((l & 1) == 1) sum += tree[l++];
if ((r & 1) == 1) sum += tree[--r];
}
return sum;
}
}
2-2) My Calendar I (LC 729) — Segment Tree / TreeMap for Interval Booking
Use TreeMap to check if new booking overlaps with any existing booking.
// LC 729 - My Calendar I
// IDEA: TreeMap — check overlap with floorKey and ceilingKey
// time = O(log N) per booking, space = O(N)
class MyCalendar {
TreeMap<Integer, Integer> calendar = new TreeMap<>();
public boolean book(int start, int end) {
Integer prev = calendar.floorKey(start);
Integer next = calendar.ceilingKey(start);
// No overlap if: prev booking ends before start, AND next booking starts after end
if ((prev == null || calendar.get(prev) <= start) &&
(next == null || next >= end)) {
calendar.put(start, end);
return true;
}
return false;
}
}
This comprehensive guide covers the essential concepts and implementations for Segment Trees and Binary Indexed Trees, with practical examples from LeetCode problems.
2-3) Segment Tree Template — Range Sum with Lazy Propagation
Full segment tree with range-update and range-query in O(log N).
// Segment Tree — Lazy Propagation Template
// time = O(log N) per update/query, space = O(N)
class SegTree {
int[] tree, lazy;
int n;
SegTree(int[] nums) {
n = nums.length;
tree = new int[4 * n];
lazy = new int[4 * n];
build(nums, 0, 0, n - 1);
}
private void build(int[] nums, int node, int l, int r) {
if (l == r) { tree[node] = nums[l]; return; }
int mid = (l + r) / 2;
build(nums, 2*node+1, l, mid);
build(nums, 2*node+2, mid+1, r);
tree[node] = tree[2*node+1] + tree[2*node+2];
}
private void pushDown(int node, int l, int r) {
if (lazy[node] != 0) {
int mid = (l + r) / 2;
tree[2*node+1] += (mid-l+1) * lazy[node]; lazy[2*node+1] += lazy[node];
tree[2*node+2] += (r-mid) * lazy[node]; lazy[2*node+2] += lazy[node];
lazy[node] = 0;
}
}
void update(int node, int l, int r, int ql, int qr, int val) {
if (ql > r || qr < l) return;
if (ql <= l && r <= qr) { tree[node] += (r-l+1)*val; lazy[node] += val; return; }
pushDown(node, l, r);
int mid = (l + r) / 2;
update(2*node+1, l, mid, ql, qr, val);
update(2*node+2, mid+1, r, ql, qr, val);
tree[node] = tree[2*node+1] + tree[2*node+2];
}
int query(int node, int l, int r, int ql, int qr) {
if (ql > r || qr < l) return 0;
if (ql <= l && r <= qr) return tree[node];
pushDown(node, l, r);
int mid = (l + r) / 2;
return query(2*node+1, l, mid, ql, qr) + query(2*node+2, mid+1, r, ql, qr);
}
}
2-4) Corporate Flight Bookings (LC 1109) — Difference Array
Range-add passengers [first, last]; prefix-sum to get totals per flight.
// LC 1109 - Corporate Flight Bookings
// IDEA: Difference array — range add O(1), prefix sum O(N) for result
// time = O(N + Q), space = O(N)
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] diff = new int[n + 1];
for (int[] b : bookings) {
diff[b[0] - 1] += b[2];
if (b[1] < n) diff[b[1]] -= b[2];
}
for (int i = 1; i < n; i++) diff[i] += diff[i-1];
return Arrays.copyOf(diff, n);
}
2-5) Count of Smaller Numbers After Self (LC 315) — Iterative Segment Tree
Build segment tree on value range; insert from right to left; query smaller prefix count.
// LC 315 - Count of Smaller Numbers After Self (Segment Tree on values)
// IDEA: Iterative seg tree on [0, 20001] value range; query prefix, then update
// time = O(N log M), space = O(M)
public List<Integer> countSmaller(int[] nums) {
int offset = 10001, size = 2 * offset + 1;
int[] tree = new int[2 * size];
Integer[] res = new Integer[nums.length];
for (int i = nums.length - 1; i >= 0; i--) {
int val = nums[i] + offset;
res[i] = queryTree(tree, size, 0, val - 1);
updateTree(tree, size, val);
}
return Arrays.asList(res);
}
private void updateTree(int[] t, int n, int i) { for (i+=n; i>0; i>>=1) t[i]++; }
private int queryTree(int[] t, int n, int l, int r) {
int s=0; for(l+=n,r+=n+1; l<r; l>>=1,r>>=1) {if((l&1)==1)s+=t[l++];if((r&1)==1)s+=t[--r];} return s;
}
2-6) Falling Squares (LC 699) — Segment Tree Max
Each square lands on the highest existing height in its range; track running max height.
// LC 699 - Falling Squares (naive O(N^2); segment tree gives O(N log N))
// IDEA: For each square compute max height in its column range, then update
// time = O(N^2), space = O(N)
public List<Integer> fallingSquares(int[][] positions) {
List<Integer> ans = new ArrayList<>();
int[] heights = new int[positions.length];
int maxH = 0;
for (int i = 0; i < positions.length; i++) {
int l = positions[i][0], sz = positions[i][1], r = l + sz;
heights[i] = sz;
for (int j = 0; j < i; j++) {
int lj = positions[j][0], rj = lj + positions[j][1];
if (lj < r && l < rj) // overlap
heights[i] = Math.max(heights[i], heights[j] + sz);
}
maxH = Math.max(maxH, heights[i]);
ans.add(maxH);
}
return ans;
}
2-7) Maximum Sum Rectangle No Larger Than K (LC 363) — Prefix Sum + TreeSet
Fix column bounds; compress to 1D row sums; use TreeSet to find max sum ≤ k.
// LC 363 - Max Sum of Rectangle No Larger Than K
// IDEA: Fix left/right cols; 1D Kadane + TreeSet for sum <= k constraint
// time = O(M^2 * N log N), space = O(N)
public int maxSumSubmatrix(int[][] matrix, int k) {
int m = matrix.length, n = matrix[0].length, ans = Integer.MIN_VALUE;
for (int l = 0; l < n; l++) {
int[] rowSum = new int[m];
for (int r = l; r < n; r++) {
for (int i = 0; i < m; i++) rowSum[i] += matrix[i][r];
TreeSet<Integer> set = new TreeSet<>();
set.add(0);
int curr = 0;
for (int s : rowSum) {
curr += s;
Integer ceiling = set.ceiling(curr - k);
if (ceiling != null) ans = Math.max(ans, curr - ceiling);
set.add(curr);
}
}
}
return ans;
}
2-8) Range Module (LC 715) — Segment Tree / TreeMap
Track which ranges are tracked; add, remove, and query ranges efficiently.
// LC 715 - Range Module (TreeMap approach)
// IDEA: TreeMap<start, end> — merge on addRange, split on removeRange
// time = O(N log N) per op, space = O(N)
class RangeModule {
TreeMap<Integer, Integer> map = new TreeMap<>();
public void addRange(int left, int right) {
Integer lo = map.floorKey(left), hi = map.floorKey(right);
if (lo != null && map.get(lo) >= left) left = lo;
if (hi != null && map.get(hi) > right) right = map.get(hi);
map.subMap(left, right).clear();
map.put(left, right);
}
public boolean queryRange(int left, int right) {
Integer lo = map.floorKey(left);
return lo != null && map.get(lo) >= right;
}
public void removeRange(int left, int right) {
Integer lo = map.floorKey(left), hi = map.floorKey(right);
if (hi != null && map.get(hi) > right) map.put(right, map.get(hi));
if (lo != null && map.get(lo) > left) map.put(lo, left);
map.subMap(left, right).clear();
}
}
2-9) Longest Increasing Subsequence (LC 300) — Segment Tree on Values
Segment tree on compressed values; query max LIS length for values < current, then update.
// LC 300 - LIS via Segment Tree (max query on value range)
// IDEA: Compress values; seg tree max query for all smaller values; update at current
// time = O(N log N), space = O(N)
public int lengthOfLIS(int[] nums) {
int[] sorted = nums.clone();
Arrays.sort(sorted);
Map<Integer, Integer> rank = new HashMap<>();
int r = 1;
for (int v : sorted) if (!rank.containsKey(v)) rank.put(v, r++);
int n = r - 1;
int[] tree = new int[2 * (n + 1)];
int ans = 0;
for (int num : nums) {
int pos = rank.get(num);
int best = qmax(tree, n, 1, pos - 1) + 1;
ans = Math.max(ans, best);
umax(tree, n, pos, best);
}
return ans;
}
private int qmax(int[] t, int n, int l, int r) { int res=0; for(l+=n,r+=n+1;l<r;l>>=1,r>>=1){if((l&1)==1)res=Math.max(res,t[l++]);if((r&1)==1)res=Math.max(res,t[--r]);}return res; }
private void umax(int[] t, int n, int i, int v) { for(i+=n;i>0;i>>=1) t[i]=Math.max(t[i],v); }
2-10) My Calendar II (LC 731) — Segment Tree / TreeMap
Allow at most 2 overlapping bookings; reject if a third overlap would occur.
// LC 731 - My Calendar II
// IDEA: Two TreeMaps — bookings and double-bookings; reject if new interval hits double-booked region
// time = O(N^2) worst, space = O(N)
class MyCalendarTwo {
List<int[]> single = new ArrayList<>(), overlap = new ArrayList<>();
public boolean book(int start, int end) {
for (int[] o : overlap)
if (o[0] < end && start < o[1]) return false;
for (int[] s : single) {
int lo = Math.max(s[0], start), hi = Math.min(s[1], end);
if (lo < hi) overlap.add(new int[]{lo, hi});
}
single.add(new int[]{start, end});
return true;
}
}