Order, Search and Range Queries¶
Table of Contents
Data Structures¶
Segment Tree:
Sum, min, max, and custom range queries.
Lazy propagation for range updates.
Variants like mergeable segment trees.
Sample implementation
Fenwick Tree (Binary Indexed Tree):
Point updates and prefix/range queries.
Multidimensional Fenwick Trees.
Sample implementation
2D: Sample implementation
Sparse Table:
Efficient for immutable data (static range queries like min, max, or GCD).
Order Statistics Tree (Augmented BST or Fenwick Tree with Order Statistics):
Find kth smallest element.
Count of elements less than or greater than a given value.
Fundamental tree algorithms
Check if a tree is a valid BST
Tree traversals with stack
RMQ (Range Minimum Query):
Hybrid solutions combining segment tree and sparse table for efficiency.
Wavelet Tree:
Handles range frequency queries and range kth order statistics.
Mo’s Algorithm:
Square-root decomposition for offline range queries.
Merge Sort Tree:
Efficient for range queries involving sorted data.
Interval Tree and KD-Tree:
For multidimensional range queries.
Monotonic Stack/Queue:
Span porblems in static data.
Augmented Trie:
For string search.
Example Problem: Design Search Autocomplete System with HitCount
Algorithms¶
Binary search
Define search space
Define condition which specifies a contiguous range in that search space touching either ends.
Decide whether to find the left boundary of that space or right.
Choose whether to search for max from left or min from right.
Application: Unsorted Array - Finding Peak Element
Application: Search Suggestions System
Find max by checking condition on solution space (Social Distancing)
Binary Search Rotated
Find Pivot
Search in rotated
Divide-and-Conquer approaches (e.g., inversion count with merge sort).
Sliding window techniques (efficient for specific range problems).
Note
fixed length
fixed sum with constant extra bookkeeping
fixed sum with auxiliary data structures
variable length
fixed sum with constant extra bookkeeping - aggregate >= value
fixed sum with auxiliary data structures - frequency, prefix sums -> dict, monotonic queue, bst
Attention
sequential grouping
sequential criteria - longest, smallest, contained, largest, smallest
Two-pointer methods for range problems in sorted data.
Offline processing for batch queries using Mo’s Algorithm or persistent data structures.
Cycle sort
Cycle detection: Floyd
Fundamental Problems¶
MEX - Minimum Excluded Element¶
Cycle sort - First missing positive in range [1,n]
Design data structure that pops smallest available numbers in infiite set with addback
Implicit MEX
1from sortedcontainers import SortedSet 2 3""" 4Think of it as if we're forming an array by calling popSmallest. 5On that array, we're calling MEX operation. 6 7Observations 8- The array isn't explicit here. 9- we know that the array doesn't contain duplicates. 10- array can only contain unique elements. 11- we won't need to store a hashmap for counts. 12 13Bookkeeping 14- keep track of the mex elements. 15- keep track of the range of popped elements 16- added back elements form holes in that range 17- anything that's in that hole is smaller than popped max 18 - needs to be sorted 19 - needs fast retrieval 20- how does addback change the popped range? 21""" 22 23class MexImplSetAndHeap: 24 def __init__(self): 25 self.is_missing = set() 26 self.missing_minheap = [] 27 self.max_added = 0 28 29 def popSmallest(self) -> int: 30 mex = self.max_added + 1 31 if self.is_missing: 32 mex = min(mex, heapq.heappop(self.missing_minheap)) 33 self.is_missing.remove(mex) 34 self.max_added = max(self.max_added, mex) 35 return mex 36 37 def addBack(self, num: int) -> None: 38 if num <= self.max_added and num not in self.is_missing: 39 self.is_missing.add(num) 40 heapq.heappush(self.missing_minheap, num) 41 42class MexImplSortedSet: 43 def __init__(self): 44 self.missing_numbers = SortedSet() 45 self.max_added = 0 46 47 def popSmallest(self) -> int: 48 mex = self.missing_numbers.pop(0) if self.missing_numbers else self.max_added + 1 49 self.max_added = max(self.max_added, mex) 50 return mex 51 52 def addBack(self, num: int) -> None: 53 if num <= self.max_added: 54 self.missing_numbers.add(num) 55 56class SmallestInfiniteSet: 57 58 def __init__(self): 59 # self.impl = MexImplSortedSet() 60 self.impl = MexImplSetAndHeap() 61 62 def popSmallest(self) -> int: 63 return self.impl.popSmallest() 64 65 def addBack(self, num: int) -> None: 66 self.impl.addBack(num) 67 68 69# Your SmallestInfiniteSet object will be instantiated and called as such: 70# obj = SmallestInfiniteSet() 71# param_1 = obj.popSmallest() 72# obj.addBack(num)
Binary Search¶
Problem: Given an array where each element represents the number of days it takes for a flower to bloom, and integers \(m\) and \(k\), find the minimum number of days required to make \(m\) bouquets, where each bouquet requires \(k\) adjacent flowers.
Hints: Use binary search on the minimum days.
Problem: Given \(n\) books and \(m\) students, where each book has a certain number of pages, partition the books such that the maximum pages assigned to a student is minimized.
Hints: Binary search on the maximum pages.
Problem: Given \(n\) piles of bananas and an integer \(h\), find the minimum eating speed \(k\) such that Koko can finish all the bananas in \(h\) hours.
Hints: Binary search on the eating speed.
Problem: Given a row-wise sorted matrix, find its median.
Hints: Use binary search on the value range, with a helper function to count elements smaller than or equal to the mid.
Problem: Given \(n\) stalls and \(c\) cows, place the cows in the stalls such that the minimum distance between any two cows is maximized.
Hints: Binary search on the minimum distance.
Problem: Given a rotated sorted array, find a target value in \(O(\log n)\).
Hints: Binary search with conditions to identify the rotated segment.
Problem: Split an array into \(m\) non-empty subarrays to minimize the largest sum among the subarrays.
Hints: Binary search on the maximum subarray sum.
Problem: Given an unsorted array, find a peak element (an element greater than its neighbors) in \(O(\log n)\).
Hints: Apply binary search with local comparison.
Problem: Given an array and queries, for each query, find the maximum number of elements in the array whose sum is less than or equal to the query value.
Hints: Binary search with prefix sums.
Problem: Given an array of integers and a number \(p\), partition the array into \(p\) pairs such that the maximum absolute difference of any pair is minimized.
Hints: Binary search on the maximum difference.
Problem: Given points on a line and a fixed number of segments, maximize the minimum distance between the segment boundaries.
Hints: Binary search on the answer.
Inversions¶
Two approaches - Two pointers, monotonic stack
1def twoPointersTwoPass(self, nums: List[int]) -> int: 2 """ 3 Note: 4 if we sort the numbers, then it is easy to find the solution 5 which involves identifying interval [i,j] where i is the 6 leftmost index where sorted[i] differs from nums[i] and j 7 is the rightmost index where sorted[j] differs from nums[j]. 8 9 The trick here is to identify i and j without sorting via understanding inversions. 10 11 Note that when we sort it, the leftmost point where nums[i] != sorted[i] 12 is where the first inversion happens - nums[i] must have gotten swapped 13 with someting smaller on the right. 14 Similarly, for the rightmost point. 15 16 We define two intervals: [0,j] and [i,n-1] and take their intersections. 17 (a) Find [0,j] => where j is the rightmost index which is inverted. 18 (b) Find [i,n-1] => where i is the leftmost index which is inverted. 19 20 For inversions, keep in mind: 21 if nums[p] <-> nums[q] is inverted for some p < q, and nums[r] > nums[p] 22 for p < r < q, then nums[r] <-> nums[p] is also inverted. 23 24 Therefore, when traversing from left to right, we just need to keep 25 track of the max we've seen from the left and find inversions on the 26 right just by comparing it with max. 27 28 Similarly, when traversing from right to left, we just need to keep 29 track of the min we've seen from right, and find inversions on the left 30 by comparisng only with it 31 """ 32 n = len(nums) 33 maxLeftIndex, minRightIndex = 0, n-1 34 rightmostInverted, leftmostInverted = -1, 0 35 for index in range(n): 36 if nums[maxLeftIndex] <= nums[index]: 37 maxLeftIndex = index 38 else: 39 # found someting smaller on the right 40 rightmostInverted = index 41 for index in range(n-1,-1,-1): 42 if nums[index] <= nums[minRightIndex]: 43 minRightIndex = index 44 else: 45 # found something larger on the left 46 leftmostInverted = index 47 return rightmostInverted - leftmostInverted + 1 48 49def monotonicStackSinglePass(self, nums: List[int]) -> int: 50 """ 51 Keep track of the index of the leftmost element popped 52 and the largest value of the popped elements so that we 53 can find the rightmost element that's inverted 54 """ 55 n = len(nums) 56 maxLeftIndex = 0 57 leftmostInverted, rightmostInverted = n, n-1 58 stack = [] 59 for index in range(n): 60 while stack and nums[stack[-1]] > nums[index]: 61 # found something larger on the left 62 leftmostInverted = min(leftmostInverted, stack.pop()) 63 if nums[maxLeftIndex] <= nums[index]: 64 maxLeftIndex = index 65 else: 66 # found something smaller on the right 67 rightmostInverted = index 68 stack.append(index) 69 return rightmostInverted - leftmostInverted + 1
Two pointers
1def findLengthOfShortestSubarray(self, arr: List[int]) -> int: 2 length = len(arr) 3 left, right = 0, len(arr)-1 4 while left < length-1 and arr[left] <= arr[left+1]: 5 left += 1 6 while right > 0 and arr[right-1] <= arr[right]: 7 right -= 1 8 if left >= right: 9 return 0 10 # print(left, right) 11 min_width = min(length - left - 1, right) 12 # print(min_width) 13 for l in range(left + 1): 14 r = right 15 while r < length and arr[l] > arr[r]: 16 r += 1 17 min_width = min(min_width, r-l-1) 18 return min_width
Order Statistics¶
Maintain the top k elements in a stream of data.
Hints: Leverage min-heaps or order statistics trees.
Use two heaps (max-heap and min-heap) for efficiency.
Given an array, for each element, count how many elements are smaller/larger to its right.
Solution: Fenwick Tree, segment tree, or merge sort.
Variants where you cannot sort directly (e.g., use Quickselect).
kth Element in the Cartesian Product
Problem: Given two sorted arrays \(A\) and \(B\), find the \(k\)-th smallest tuple \((a, b)\) in \(A \times B\) under the order relation defined above (based on the sum \(a + b\)).
Hints: Use a min-heap with tuples to track possible combinations efficiently.
Problem: Given an array of integers and a sliding window of size \(k\), find the median of each window as it slides from left to right.
Hints: Use two heaps (max-heap and min-heap) to dynamically maintain the window.
Problem: For an array \(A\), process \(q\) queries of the form \((L, R)\) where you need to count the number of inversions in the subarray \(A[L:R]\).
Hints: Use a segment tree with merge-sort logic at each node.
Range k-th Smallest Element
Problem: Given an array and \(q\) queries of the form \((L, R, k)\), find the \(k\)-th smallest element in the range \([L, R]\).
Hints: Use a merge sort tree or wavelet tree for efficient query processing.
Count of Numbers in Range with a Given Frequency
Problem: Given an array and \(q\) queries of the form \((L, R, F)\), count how many numbers in the range \([L, R]\) appear exactly \(F\) times.
Hints: Use Mo’s Algorithm with frequency tracking or segment trees with custom nodes.
Range Query Problems¶
Range Sum Query with Updates
Hints: Solve using segment trees or Fenwick trees with range updates.
Range Minimum/Maximum Query
Hints: Solve using segment trees, sparse tables, or hybrid methods.
Dynamic Range Median Queries
Hints: Maintain a dynamic dataset and answer queries for the median of a range.
Range XOR Query
Hints: Solve using segment trees.
Sum of Range Products
Hints: Given an array, answer the sum of products of all pairs in the range [L, R].
Number of Distinct Elements in Range
Hints: Use Mo’s Algorithm or a segment tree with a map structure.
Range Frequency Query
Hints: Solve using a wavelet tree or merge sort tree.
Dynamic Range Median Queries
Problem: Maintain a dynamic array supporting
Insertion of an element.
Deletion of an element.
Querying the median of any range \([L, R]\).
Hints: Combine balanced BST or heaps with a range query structure like segment trees.
Range XOR with Updates
Problem: Given an array of integers, process the following operations efficiently
Update the \(i\) -th element to \(x\).
Query the XOR of elements in the range \([L, R]\).
Hints: Use a segment tree with XOR as the operation and point updates.
Maximum Frequency in a Range
Problem: Given an array and \(q\) queries of the form \((L, R)\), find the most frequent number in the range \([L, R]\).
Hints: Use a segment tree with frequency maps stored at each node.
Maximum Subarray Sum in a Range
Problem: Process queries of the form \((L, R)\), where you must find the maximum subarray sum in the range \([L, R]\).
Hints: Augment the segment tree to store max subarray sums and handle overlapping subranges efficiently.
Range Updates with a Custom Function
Problem: Design a data structure to efficiently handle
Updates: Apply a custom function \(f(x)\) to all elements in the range \([L, R]\).
Queries: Retrieve the sum of all elements in the range \([L, R]\).
Hints: Use a segment tree with lazy propagation where \(f(x)\) can be propagated efficiently.
Hybrid Problems¶
Dynamic Skyline Problem
Given a list of intervals, dynamically insert or delete intervals and determine the current skyline.
Maximum Sum Rectangle in a 2D Matrix
Use a 1D segment tree approach for optimal results.
Range GCD Query
Find the GCD of elements in the range [L, R] using a segment tree or sparse table.
Number of Rectangles Containing a Point
Problem: You are given a list of \(n\) rectangles (defined by two opposite corners) and \(q\) points. For each point, count how many rectangles contain it.
Hints: Use a segment tree or 2D Fenwick Tree to maintain active ranges as you sweep through one coordinate.
Dynamic Skyline
Problem: Maintain the skyline (maximum height of buildings seen from a distance) as you dynamically add and remove buildings.
Hints: Use an interval tree or segment tree to handle dynamic range updates efficiently.
Count Subarrays with Given Sum in Range
Problem: For \(q\) queries \((L, R, S)\), count how many contiguous subarrays in the range \([L, R]\) have a sum equal to \(S\).
Hints: Use prefix sums with a Fenwick Tree to count valid subarray sums efficiently.
Maximum Overlap of Intervals
Problem: Given a list of intervals, process \(q\) queries to find the maximum overlap of intervals in a given range \([L, R]\).
Hints: Use a difference array combined with prefix sums or a segment tree for dynamic updates.
Submatrix Sum Queries
Problem: Given a 2D grid, process
Updates: Add a value to all elements in a submatrix.
Queries: Find the sum of elements in any submatrix.
Hints: Use a 2D Fenwick Tree or segment tree for efficient query and update operations.
Problems Using Monotonic Stack¶
Largest Rectangle in Histogram
Problem: Given an array of heights representing a histogram, find the area of the largest rectangle.
Hints: Use a monotonic stack to track bars in increasing order.
Sample implementation
Related: Maximum rectangle in binary matrix. Can be reduced to above.
Trapping Rain Water
Problem: Given an array representing heights, calculate how much water can be trapped after it rains.
Hints: Use a monotonic stack to find the bounds of trapped water.
Next Greater Element (NGE)
Problem: For an array, find the next greater element for each element.
Hints: Traverse from the end and use a monotonic stack to maintain greater elements.
Next Smaller Element
Problem: For an array, find the next smaller element for each element.
Hints: Similar to NGE, but with a decreasing monotonic stack.
Sum of Subarray Minimums
Problem: Given an array, find the sum of the minimum values of all subarrays.
Hints: Use a monotonic stack to find the nearest smaller elements on both sides.
132 Pattern
Problem: Find if there exists a 132 pattern in an array.
Hints: Use a monotonic stack to maintain potential “3” values while iterating.
Daily Temperatures
Problem: For each day’s temperature, find how many days you’d have to wait for a warmer temperature.
Hints: Monotonic stack tracks indices of temperatures.
Asteroid Collision
Problem: Simulate asteroid collisions where larger ones destroy smaller ones.
Hints: Use a monotonic stack to simulate collisions.
Problems Using Monotonic Queue¶
Problem: Find the maximum element in every sliding window of size \(k\).
Hints: Maintain a monotonic queue to store potential maxima.
Problem: Given an array, find the shortest subarray with a sum \(\geq K\).
Hints: Use a monotonic queue to optimize prefix sums.
Monotonic queue for rightmost left index
1from itertools import accumulate 2class Solution: 3 def shortestSubarray(self, nums: List[int], k: int) -> int: 4 n = len(nums) 5 cumsum = [x for x in accumulate(nums)] 6 print(cumsum) 7 # for each r, find the largest l in [0,r-1] such that 8 # cumsum[r]-cumsum[l] >= k, or cumsum[l] <= cumsum[r]-k 9 minLen = 2*n 10 minQ = deque() 11 for r in range(n): 12 minLen = min(minLen, r+1) if cumsum[r] >= k else minLen 13 while minQ and cumsum[minQ[0]] <= cumsum[r]-k: 14 minLen = min(minLen, r-minQ[0]) 15 minQ.popleft() 16 while minQ and cumsum[minQ[-1]] > cumsum[r]: 17 minQ.pop() 18 minQ.append(r) 19 return minLen if minLen < 2*n else -1This can also be solved using segment tree but it’s suboptimal
Interview Problems¶
Sliding Window Maximum
Basic Variant
Problem: Find the maximum element in every sliding window of size \(k\) in an array.
Hints: Use a monotonic deque to store indices of potential maxima, maintaining decreasing order.
Dynamic Data (Real-Time Updates)
Change: The array is dynamic, and elements can be added/removed in real-time.
Hints: Use a Segment Tree or Fenwick Tree to track maxima in specific ranges.
Multiple Queries
Change: Instead of just one pass, answer multiple queries of the form \([L, R]\) to find the maximum in subarrays.
Hints: Preprocess with a Sparse Table (for static queries) or Segment Tree (for dynamic updates).
Largest Rectangle in Histogram
Basic Variant
Problem: Find the area of the largest rectangle that can be formed in a histogram.
Hints: Use a monotonic stack to find the next smaller and previous smaller heights for each bar.
2D Matrix (Maximal Rectangle)
Change: Extend the to a binary matrix to find the largest rectangle containing only 1s.
Hints: Treat each row as a histogram and use the stack approach iteratively.
Dynamic Histogram Updates
Change: Allow updates to histogram heights and dynamically compute the largest rectangle.
Hints: Use a Segment Tree to store and query the largest rectangle efficiently.
Trapping Rain Water
Basic Variant
Problem: Given an array of heights, calculate the total water trapped after rain.
Hints: Use two-pointer technique or monotonic stack to find bounds for water levels.
Dynamic Updates
Change: Heights can be updated, and the total trapped water must be recalculated efficiently.
Hints: Use a Fenwick Tree to maintain prefix max values and efficiently compute water levels.
Multiple Queries
Change: For multiple ranges \([L, R]\), calculate the water trapped in those ranges.
Hints: Precompute prefix max/min values for efficient range queries.
Next Greater Element (NGE)
Basic Variant
Problem: For an array, find the next greater element for each element.
Hints: Use a monotonic stack while iterating from the end of the array.
Circular Array
Change: The array is circular, so elements wrap around.
Hints: Simulate wrapping by iterating twice through the array with a stack.
Dynamic Updates
Change: Support updates to the array and answer NGE queries efficiently.
Hints: Use a Segment Tree or Ordered Set to dynamically track and query next greater elements.
Range Sum Query
Basic Variant
Problem: Given an array, calculate the sum of elements in a range \([L, R]\) .
Hints: Use a prefix sum array for efficient range queries.
Dynamic Updates
Change: Allow updates to the array and answer range sum queries.
Hints: Use a Fenwick Tree or Segment Tree for \(O(\log n)\) updates and queries.
Range Sum with Modulo or Constraints
Change: Add a constraint to compute range sums modulo \(k\), or find if the sum in a range satisfies certain conditions.
Hints: Use a Segment Tree with custom lazy propagation to handle constraints.
Stock Span Problem
Basic Variant
Problem: For each day’s stock price, find the number of consecutive days before it with a price less than or equal to the current day.
Hints: Use a monotonic stack to track indices.
Dynamic Price Updates
Change: Allow updates to stock prices and recalculate the span dynamically.
Hints: Use a Segment Tree to maintain range queries for stock prices.
Multiple Queries for Ranges
Change: Answer span queries for multiple subranges \([L, R]\) .
Hints: Combine Segment Tree or Sparse Table with preprocessing for efficient queries.
Sum of Subarray Minimums
Basic Variant
Problem: Find the sum of minimum values of all subarrays of an array.
Hints: Use a monotonic stack to find the nearest smaller elements on both sides.
Dynamic Array Updates
Change: Support updates to array elements and recompute the sum of subarray minimums.
Hints: Use a Segment Tree to track minimums and their contributions dynamically.
Additional Constraints
Change: Add constraints like subarray sums must be within a given range or subarray lengths must be limited.
Hints: Combine a Fenwick Tree with constraint checks for efficient processing.
Binary Search Variants
Basic Variant
Problem: Find an element in a sorted array using binary search.
Hints: Divide and conquer to find the target element.
Rotated Sorted Array
Change: The array is rotated; find the target element.
Hints: Modify binary search to handle rotations.
Minimum in Rotated Sorted Array with Duplicates
Change: The rotated array contains duplicates.
Hints: Adapt binary search with careful handling of duplicate elements.
Find Median in a Stream
Change: Support dynamic updates and find the median efficiently.
Hints: Use a combination of Heaps or Balanced BSTs for dynamic median maintenance.