2503. Maximum Number of Points From Grid Queries
Problem Description
You have an m x n
matrix called grid
filled with integers, and you need to process k
queries stored in an array queries
.
For each query value queries[i]
, you start at the top-left corner of the matrix (position [0,0]
) and explore the grid following these rules:
- From your current cell, you can only move to an adjacent cell (up, down, left, or right) if
queries[i]
is strictly greater than the value in your current cell - When you visit a cell for the first time during this query, you earn 1 point
- If you revisit a cell, you don't earn additional points
- The exploration stops when you can't move anymore (when
queries[i]
is not greater than the current cell value)
Your task is to return an array answer
where answer[i]
represents the maximum number of points you can earn for queries[i]
.
Key Points:
- Each query is processed independently - you always start fresh from
[0,0]
- You can visit the same cell multiple times during one query, but only score points on the first visit
- Movement is only allowed to adjacent cells (4-directional: up, down, left, right)
- Movement requires the query value to be strictly greater than the current cell's value
Example:
If you have a 3x3 grid and a query value of 5, you start at [0,0]
. If grid[0][0] = 2
, since 5 > 2, you get 1 point and can move to adjacent cells. You continue exploring, earning points for each new cell you visit where the cell value is less than 5.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The grid can be viewed as a graph where each cell is a node, and adjacent cells (up, down, left, right) are connected by edges. We need to traverse this graph starting from the top-left corner.
Is it a tree?
- No: The grid structure is not a tree - it can have cycles when we consider the connections between adjacent cells.
Is the problem related to directed acyclic graphs?
- No: The grid allows movement in all four directions based on value comparisons, and we can revisit cells, so it's not specifically about DAGs.
Is the problem related to shortest paths?
- Yes: While not explicitly asking for shortest paths, we need to explore all reachable cells from the starting point. The problem involves finding all cells reachable from
[0,0]
where the cell value is less than the query value. This is similar to finding all nodes within a certain "distance" (value threshold) from the source.
Is the graph Weighted?
- No: The edges between adjacent cells don't have weights. The cell values act as constraints for traversal rather than edge weights. We either can move to an adjacent cell (if query > cell value) or we cannot.
BFS
- Yes: Since we have an unweighted graph exploration problem where we need to find all reachable cells from a starting point based on a condition, BFS is the appropriate choice.
Conclusion: The flowchart suggests using BFS (Breadth-First Search) for this problem. The solution uses BFS with a priority queue (min-heap) to efficiently process cells in order of their values, allowing us to handle multiple queries by processing them in sorted order and expanding our explored region incrementally.
Intuition
The key insight is recognizing that for each query, we're essentially asking: "How many cells can I reach from [0,0]
if I can only step on cells with values less than my query value?"
If we process queries independently, we'd need to run a BFS for each query, which would be inefficient. However, notice that if we can reach a set of cells for query value x
, we can definitely reach those same cells (and possibly more) for any query value y
where y > x
. This monotonic property suggests we can reuse our work.
Instead of starting fresh for each query, what if we process queries in ascending order? For a smaller query value, we explore and count reachable cells. When we move to a larger query value, we can continue from where we left off, expanding our explored region to include cells with values less than the new query threshold.
Think of it like water flooding a terrain: if water level is at height 3, it covers all land below height 3. When water rises to height 5, it keeps everything it already covered and floods additional land between heights 3 and 5.
To implement this efficiently, we use a min-heap to always know the next smallest unvisited cell value. We start from [0,0]
and gradually expand our explored region. For each sorted query:
- Pop all cells from the heap whose values are less than the current query value
- Mark them as visited and increment our count
- Add their unvisited neighbors to the heap
- The current count is the answer for this query
By processing queries in sorted order but maintaining their original indices, we can fill in the answer array correctly. This approach transforms multiple BFS runs into a single, incremental exploration of the grid, dramatically improving efficiency from O(k * m * n)
to O(m * n * log(m * n) + k * log k)
where k
is the number of queries.
Learn more about Breadth-First Search, Union Find, Two Pointers, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The implementation follows the offline query processing pattern with BFS using a priority queue (min-heap):
1. Query Preprocessing:
First, we sort the queries while preserving their original indices. We create a list of tuples qs = sorted((v, i) for i, v in enumerate(queries))
where each tuple contains (query_value, original_index)
. This allows us to process queries in ascending order while still being able to place answers in the correct positions.
2. Data Structures Setup:
- Min-Heap
q
: Initialized with the starting cell[(grid[0][0], 0, 0)]
containing(cell_value, row, col)
- Visited Matrix
vis
: A 2D boolean array to track which cells have been visited, initially allFalse
exceptvis[0][0] = True
- Counter
cnt
: Tracks the number of cells visited so far - Answer Array
ans
: Initialized with zeros, same length as queries
3. Processing Queries in Order:
For each sorted query (v, k)
where v
is the query value and k
is the original index:
while q and q[0][0] < v: _, i, j = heappop(q) cnt += 1 # Explore 4 adjacent cells
This loop continues popping cells from the heap as long as the minimum cell value is less than the current query value v
. Each popped cell contributes 1 to our count.
4. Exploring Adjacent Cells:
For each popped cell at position (i, j)
, we check all 4 adjacent cells:
for a, b in pairwise((-1, 0, 1, 0, -1)): x, y = i + a, j + b
The pairwise
function with (-1, 0, 1, 0, -1)
generates the pairs: (-1, 0)
, (0, 1)
, (1, 0)
, (0, -1)
, representing movements up, right, down, and left respectively.
5. Adding New Cells to Heap: For each valid adjacent cell that hasn't been visited:
- Check boundaries:
0 <= x < m and 0 <= y < n
- Check if unvisited:
not vis[x][y]
- If valid, add to heap:
heappush(q, (grid[x][y], x, y))
- Mark as visited:
vis[x][y] = True
6. Recording Answers:
After processing all cells with values less than the current query v
, we record ans[k] = cnt
. The count cnt
accumulates across queries since larger queries include all cells accessible by smaller queries.
Time Complexity: O(m * n * log(m * n) + k * log k)
where:
m * n * log(m * n)
for heap operations on all grid cellsk * log k
for sorting queries
Space Complexity: O(m * n)
for the visited matrix and heap storage.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
- Grid (3x3):
[1, 3, 5] [2, 6, 4] [7, 8, 9]
- Queries:
[4, 6, 2]
Step 1: Query Preprocessing Sort queries with original indices:
qs = [(2, 2), (4, 0), (6, 1)]
- This means: process value 2 (originally at index 2), then 4 (index 0), then 6 (index 1)
Step 2: Initial Setup
- Min-heap:
[(1, 0, 0)]
(starting cell value=1 at position [0,0]) - Visited:
vis[0][0] = True
, all othersFalse
- Counter:
cnt = 0
- Answer array:
ans = [0, 0, 0]
Step 3: Process Query value=2 (original index 2)
- While heap top (1) < 2:
- Pop
(1, 0, 0)
, incrementcnt = 1
- Check adjacents:
- Right [0,1]: value=3, not visited β push
(3, 0, 1)
to heap, mark visited - Down [1,0]: value=2, not visited β push
(2, 1, 0)
to heap, mark visited
- Right [0,1]: value=3, not visited β push
- Heap now:
[(2, 1, 0), (3, 0, 1)]
- Pop
- Heap top (2) is NOT < 2, so stop
ans[2] = 1
(we visited 1 cell with value < 2)
Step 4: Process Query value=4 (original index 0)
- While heap top (2) < 4:
- Pop
(2, 1, 0)
, incrementcnt = 2
- Check adjacents:
- Right [1,1]: value=6, not visited β push
(6, 1, 1)
to heap, mark visited - Down [2,0]: value=7, not visited β push
(7, 2, 0)
to heap, mark visited
- Right [1,1]: value=6, not visited β push
- Heap now:
[(3, 0, 1), (6, 1, 1), (7, 2, 0)]
- Pop
- Continue while heap top (3) < 4:
- Pop
(3, 0, 1)
, incrementcnt = 3
- Check adjacents:
- Right [0,2]: value=5, not visited β push
(5, 0, 2)
to heap, mark visited - Down [1,1]: already visited, skip
- Right [0,2]: value=5, not visited β push
- Heap now:
[(5, 0, 2), (6, 1, 1), (7, 2, 0)]
- Pop
- Heap top (5) is NOT < 4, so stop
ans[0] = 3
(we've visited 3 cells total with value < 4)
Step 5: Process Query value=6 (original index 1)
- While heap top (5) < 6:
- Pop
(5, 0, 2)
, incrementcnt = 4
- Check adjacents:
- Down [1,2]: value=4, not visited β push
(4, 1, 2)
to heap, mark visited
- Down [1,2]: value=4, not visited β push
- Heap now:
[(4, 1, 2), (6, 1, 1), (7, 2, 0)]
- Pop
- Continue while heap top (4) < 6:
- Pop
(4, 1, 2)
, incrementcnt = 5
- Check adjacents:
- Left [1,1]: already visited, skip
- Down [2,2]: value=9, not visited β push
(9, 2, 2)
to heap, mark visited
- Heap now:
[(6, 1, 1), (7, 2, 0), (9, 2, 2)]
- Pop
- Heap top (6) is NOT < 6, so stop
ans[1] = 5
(we've visited 5 cells total with value < 6)
Final Answer: [3, 5, 1]
- Query 4: can reach 3 cells (values 1, 2, 3)
- Query 6: can reach 5 cells (values 1, 2, 3, 5, 4)
- Query 2: can reach 1 cell (value 1)
Solution Implementation
1from typing import List
2from heapq import heappush, heappop
3
4class Solution:
5 def maxPoints(self, grid: List[List[int]], queries: List[int]) -> List[int]:
6 rows, cols = len(grid), len(grid[0])
7
8 # Sort queries with their original indices to process in ascending order
9 sorted_queries = sorted((value, index) for index, value in enumerate(queries))
10
11 # Initialize result array to store answers in original query order
12 result = [0] * len(sorted_queries)
13
14 # Min heap to store cells to explore: (cell_value, row, col)
15 # Start from top-left corner (0, 0)
16 min_heap = [(grid[0][0], 0, 0)]
17
18 # Counter for reachable cells
19 reachable_count = 0
20
21 # Track visited cells to avoid revisiting
22 visited = [[False] * cols for _ in range(rows)]
23 visited[0][0] = True
24
25 # Process each query in ascending order
26 for query_value, original_index in sorted_queries:
27 # Expand reachable area: process all cells with value < query_value
28 while min_heap and min_heap[0][0] < query_value:
29 cell_value, row, col = heappop(min_heap)
30 reachable_count += 1
31
32 # Check all 4 adjacent cells (up, right, down, left)
33 directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
34 for row_delta, col_delta in directions:
35 new_row, new_col = row + row_delta, col + col_delta
36
37 # Add valid unvisited neighbors to the heap
38 if (0 <= new_row < rows and
39 0 <= new_col < cols and
40 not visited[new_row][new_col]):
41 heappush(min_heap, (grid[new_row][new_col], new_row, new_col))
42 visited[new_row][new_col] = True
43
44 # Store the count for this query at its original position
45 result[original_index] = reachable_count
46
47 return result
48
1class Solution {
2 public int[] maxPoints(int[][] grid, int[] queries) {
3 int queryCount = queries.length;
4
5 // Create array to store query value and original index
6 int[][] queryWithIndex = new int[queryCount][2];
7 for (int i = 0; i < queryCount; ++i) {
8 queryWithIndex[i] = new int[] {queries[i], i};
9 }
10
11 // Sort queries by value in ascending order
12 Arrays.sort(queryWithIndex, (a, b) -> a[0] - b[0]);
13
14 // Initialize result array
15 int[] result = new int[queryCount];
16
17 // Grid dimensions
18 int rows = grid.length;
19 int cols = grid[0].length;
20
21 // Track visited cells
22 boolean[][] visited = new boolean[rows][cols];
23 visited[0][0] = true;
24
25 // Min heap to process cells by their values
26 // Each element: [cell value, row index, col index]
27 PriorityQueue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
28 minHeap.offer(new int[] {grid[0][0], 0, 0});
29
30 // Direction vectors for exploring adjacent cells (up, right, down, left)
31 int[] directions = new int[] {-1, 0, 1, 0, -1};
32
33 // Count of cells that can be reached
34 int reachableCount = 0;
35
36 // Process each query in sorted order
37 for (int[] queryInfo : queryWithIndex) {
38 int queryValue = queryInfo[0];
39 int originalIndex = queryInfo[1];
40
41 // Process all cells with value less than current query
42 while (!minHeap.isEmpty() && minHeap.peek()[0] < queryValue) {
43 int[] currentCell = minHeap.poll();
44 reachableCount++;
45
46 // Explore all 4 adjacent cells
47 for (int dir = 0; dir < 4; ++dir) {
48 int newRow = currentCell[1] + directions[dir];
49 int newCol = currentCell[2] + directions[dir + 1];
50
51 // Check if the new position is valid and unvisited
52 if (newRow >= 0 && newRow < rows &&
53 newCol >= 0 && newCol < cols &&
54 !visited[newRow][newCol]) {
55
56 visited[newRow][newCol] = true;
57 minHeap.offer(new int[] {grid[newRow][newCol], newRow, newCol});
58 }
59 }
60 }
61
62 // Store the count at the original query index
63 result[originalIndex] = reachableCount;
64 }
65
66 return result;
67 }
68}
69
1class Solution {
2public:
3 // Direction vectors for moving up, right, down, left
4 const int directions[5] = {-1, 0, 1, 0, -1};
5
6 vector<int> maxPoints(vector<vector<int>>& grid, vector<int>& queries) {
7 int numQueries = queries.size();
8
9 // Create pairs of (query value, original index) for sorting
10 vector<pair<int, int>> sortedQueries(numQueries);
11 for (int i = 0; i < numQueries; ++i) {
12 sortedQueries[i] = {queries[i], i};
13 }
14
15 // Sort queries by value to process them in ascending order
16 sort(sortedQueries.begin(), sortedQueries.end());
17
18 // Result array to store answers in original query order
19 vector<int> result(numQueries);
20
21 int rows = grid.size();
22 int cols = grid[0].size();
23
24 // Visited array to track processed cells
25 bool visited[rows][cols];
26 memset(visited, 0, sizeof visited);
27
28 // Mark starting position as visited
29 visited[0][0] = true;
30
31 // Min heap to store cells by their values
32 // Tuple contains: (cell value, row index, column index)
33 priority_queue<tuple<int, int, int>,
34 vector<tuple<int, int, int>>,
35 greater<tuple<int, int, int>>> minHeap;
36
37 // Start from top-left corner
38 minHeap.push({grid[0][0], 0, 0});
39
40 // Counter for cells that can be reached
41 int reachableCount = 0;
42
43 // Process each query in sorted order
44 for (auto& queryPair : sortedQueries) {
45 int threshold = queryPair.first;
46 int originalIndex = queryPair.second;
47
48 // Process all cells with values less than current threshold
49 while (!minHeap.empty() && get<0>(minHeap.top()) < threshold) {
50 auto [cellValue, row, col] = minHeap.top();
51 minHeap.pop();
52 ++reachableCount;
53
54 // Explore all 4 adjacent cells
55 for (int dir = 0; dir < 4; ++dir) {
56 int newRow = row + directions[dir];
57 int newCol = col + directions[dir + 1];
58
59 // Check if the new position is valid and unvisited
60 if (newRow >= 0 && newRow < rows &&
61 newCol >= 0 && newCol < cols &&
62 !visited[newRow][newCol]) {
63
64 visited[newRow][newCol] = true;
65 minHeap.push({grid[newRow][newCol], newRow, newCol});
66 }
67 }
68 }
69
70 // Store the count for this query at its original position
71 result[originalIndex] = reachableCount;
72 }
73
74 return result;
75 }
76};
77
1// Direction vectors for moving up, right, down, left
2const directions: number[] = [-1, 0, 1, 0, -1];
3
4function maxPoints(grid: number[][], queries: number[]): number[] {
5 const numQueries = queries.length;
6
7 // Create pairs of [query value, original index] for sorting
8 const sortedQueries: [number, number][] = [];
9 for (let i = 0; i < numQueries; i++) {
10 sortedQueries.push([queries[i], i]);
11 }
12
13 // Sort queries by value to process them in ascending order
14 sortedQueries.sort((a, b) => a[0] - b[0]);
15
16 // Result array to store answers in original query order
17 const result: number[] = new Array(numQueries).fill(0);
18
19 const rows = grid.length;
20 const cols = grid[0].length;
21
22 // Visited array to track processed cells
23 const visited: boolean[][] = Array(rows)
24 .fill(null)
25 .map(() => Array(cols).fill(false));
26
27 // Mark starting position as visited
28 visited[0][0] = true;
29
30 // Min heap to store cells by their values
31 // Each element contains: [cell value, row index, column index]
32 const minHeap: [number, number, number][] = [];
33
34 // Helper function to maintain heap property when inserting
35 const heapPush = (item: [number, number, number]) => {
36 minHeap.push(item);
37 minHeap.sort((a, b) => a[0] - b[0]);
38 };
39
40 // Helper function to remove minimum element from heap
41 const heapPop = (): [number, number, number] | undefined => {
42 return minHeap.shift();
43 };
44
45 // Start from top-left corner
46 heapPush([grid[0][0], 0, 0]);
47
48 // Counter for cells that can be reached
49 let reachableCount = 0;
50
51 // Process each query in sorted order
52 for (const queryPair of sortedQueries) {
53 const threshold = queryPair[0];
54 const originalIndex = queryPair[1];
55
56 // Process all cells with values less than current threshold
57 while (minHeap.length > 0 && minHeap[0][0] < threshold) {
58 const [cellValue, row, col] = heapPop()!;
59 reachableCount++;
60
61 // Explore all 4 adjacent cells
62 for (let dir = 0; dir < 4; dir++) {
63 const newRow = row + directions[dir];
64 const newCol = col + directions[dir + 1];
65
66 // Check if the new position is valid and unvisited
67 if (newRow >= 0 && newRow < rows &&
68 newCol >= 0 && newCol < cols &&
69 !visited[newRow][newCol]) {
70
71 visited[newRow][newCol] = true;
72 heapPush([grid[newRow][newCol], newRow, newCol]);
73 }
74 }
75 }
76
77 // Store the count for this query at its original position
78 result[originalIndex] = reachableCount;
79 }
80
81 return result;
82}
83
Time and Space Complexity
Time Complexity: O(k Γ log k + m Γ n Γ log(m Γ n))
The time complexity consists of two main parts:
- Sorting the queries array takes
O(k Γ log k)
wherek
is the number of queries - Processing the grid using a min-heap where each cell is visited at most once. Since we have
m Γ n
cells total, and each heap operation (push/pop) takesO(log(heap_size))
time, where the heap can contain at mostm Γ n
elements, the grid traversal takesO(m Γ n Γ log(m Γ n))
Space Complexity: O(m Γ n)
The space complexity is dominated by:
- The
vis
(visited) array which stores a boolean value for each cell:O(m Γ n)
- The heap
q
which can contain at mostm Γ n
elements (one for each cell):O(m Γ n)
- The sorted queries list and answer array:
O(k)
Since m Γ n
is typically larger than k
, the overall space complexity is O(m Γ n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Marking Cells as Visited at the Wrong Time
The Pitfall: A common mistake is marking cells as visited only when they are popped from the heap (when processing them), rather than when they are first added to the heap.
Why It's Wrong:
# INCORRECT approach: while min_heap and min_heap[0][0] < query_value: cell_value, row, col = heappop(min_heap) if visited[row][col]: # Skip if already visited continue visited[row][col] = True # Mark as visited when popped reachable_count += 1 for row_delta, col_delta in directions: new_row, new_col = row + row_delta, col + col_delta if (0 <= new_row < rows and 0 <= new_col < cols): heappush(min_heap, (grid[new_row][new_col], new_row, new_col)) # NOT marking as visited here!
This leads to:
- Duplicate entries in the heap: The same cell can be added multiple times from different neighbors
- Incorrect counting: When duplicates are popped, they might be counted multiple times
- Performance degradation: The heap grows unnecessarily large with duplicates
The Solution: Always mark cells as visited immediately when adding them to the heap:
# CORRECT approach: if (0 <= new_row < rows and 0 <= new_col < cols and not visited[new_row][new_col]): heappush(min_heap, (grid[new_row][new_col], new_row, new_col)) visited[new_row][new_col] = True # Mark immediately when added
2. Resetting State Between Queries
The Pitfall: Another common mistake is resetting the visited matrix or reachable count between queries, thinking each query should be processed independently from scratch.
Why It's Wrong:
# INCORRECT approach:
for query_value, original_index in sorted_queries:
visited = [[False] * cols for _ in range(rows)] # Reset visited
reachable_count = 0 # Reset count
min_heap = [(grid[0][0], 0, 0)] # Reset heap
# ... process query
This breaks the optimization because:
- Since queries are sorted in ascending order, cells reachable by a smaller query are also reachable by all larger queries
- Resetting would require re-exploring the same cells multiple times, defeating the purpose of sorting queries
The Solution: Maintain cumulative state across queries. The visited cells and count accumulate as we process larger queries, which is why we sort them first. Each larger query inherits all cells accessible by smaller queries plus any additional cells it can reach.
What's the output of running the following function using input [30, 20, 10, 100, 33, 12]
?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
8
1public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
12
1class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85
Recommended Readings
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Union Find Disjoint Set Union Data Structure Introduction Prerequisite Depth First Search Review problems dfs_intro Once we have a strong grasp of recursion and Depth First Search we can now introduce Disjoint Set Union DSU This data structure is motivated by the following problem Suppose we have sets of elements
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Want a Structured Path to Master System Design Too? Donβt Miss This!