2711. Difference of Number of Distinct Values on Diagonals
Problem Description
You are given a 2D grid of size m x n
containing integer values. Your task is to create a new matrix called answer
of the same size where each cell contains a calculated value based on diagonal elements.
For each cell grid[r][c]
, you need to:
-
Count distinct values on the top-left diagonal: Look at all cells that lie on the same diagonal line going towards the top-left direction from
grid[r][c]
. Count how many distinct values appear on this diagonal, excludinggrid[r][c]
itself. Call this countleftAbove[r][c]
. -
Count distinct values on the bottom-right diagonal: Look at all cells that lie on the same diagonal line going towards the bottom-right direction from
grid[r][c]
. Count how many distinct values appear on this diagonal, excludinggrid[r][c]
itself. Call this countrightBelow[r][c]
. -
Calculate the answer: The value at
answer[r][c]
is the absolute difference between these two counts:|leftAbove[r][c] - rightBelow[r][c]|
.
A diagonal line in the matrix starts from either the topmost row or the leftmost column and extends in the bottom-right direction until it reaches the boundary of the matrix.
For example, if you're at position (2, 3)
:
- The top-left diagonal would include cells at positions like
(1, 2)
,(0, 1)
(if they exist) - The bottom-right diagonal would include cells at positions like
(3, 4)
,(4, 5)
(if they exist)
The solution iterates through each cell in the grid. For each cell (i, j)
, it:
- Traverses diagonally up-left to collect all values and counts distinct ones
- Traverses diagonally down-right to collect all values and counts distinct ones
- Calculates the absolute difference between these two counts
- Stores this difference in the corresponding position of the answer matrix
Intuition
The problem asks us to examine diagonal patterns for each cell, which immediately suggests we need to traverse diagonals in two directions from each position.
The key insight is that for any cell (i, j)
, the cells on its diagonal follow a simple pattern:
- Moving up-left means decreasing both row and column indices by 1:
(i-1, j-1)
,(i-2, j-2)
, etc. - Moving down-right means increasing both row and column indices by 1:
(i+1, j+1)
,(i+2, j+2)
, etc.
Since we need to count distinct values along each diagonal direction, using a set data structure is natural - it automatically handles duplicates for us. We can traverse in each direction, add values to a set, and the size of the set gives us the count of distinct values.
The straightforward approach is to:
- For each cell in the grid, treat it as our reference point
- Walk diagonally up-left from
(i, j)
by repeatedly doingx = x - 1, y = y - 1
until we hit the grid boundary (when eitherx
becomes 0 ory
becomes 0) - Collect all values encountered in a set to get the distinct count
- Similarly, walk diagonally down-right from
(i, j)
by repeatedly doingx = x + 1, y = y + 1
until we hit the grid boundary (when eitherx
reachesm-1
ory
reachesn-1
) - The absolute difference between these two counts gives us our answer for that cell
This direct simulation approach works well because we're simply implementing exactly what the problem describes - no need for complex preprocessing or optimization. The use of sets naturally handles the "distinct values" requirement, and the diagonal traversal is straightforward with coordinate manipulation.
Solution Approach
The solution implements a direct simulation approach by iterating through each cell in the grid and calculating the required values for that position.
Main Algorithm Structure:
-
Initialize the result matrix: Create an
m x n
matrixans
filled with zeros to store our answers. -
Iterate through each cell: Use nested loops to visit each cell
(i, j)
in the grid. -
Count distinct values on the top-left diagonal:
- Start from the current position
(i, j)
by settingx = i, y = j
- Create an empty set
s
to track distinct values - Move diagonally up-left using a while loop:
while x and y:
- First move one step:
x = x - 1, y = y - 1
- Add the value at this new position to the set:
s.add(grid[x][y])
- First move one step:
- The loop continues while both
x > 0
andy > 0
(sincex and y
evaluates toTrue
only when both are non-zero) - Store the count of distinct values:
tl = len(s)
- Start from the current position
-
Count distinct values on the bottom-right diagonal:
- Reset to the current position:
x = i, y = j
- Create a new empty set
s
- Move diagonally down-right using a while loop:
while x + 1 < m and y + 1 < n:
- First move one step:
x = x + 1, y = y + 1
- Add the value at this new position to the set:
s.add(grid[x][y])
- First move one step:
- The loop continues while we can move without going out of bounds
- Store the count of distinct values:
br = len(s)
- Reset to the current position:
-
Calculate and store the result:
- Compute the absolute difference:
ans[i][j] = abs(tl - br)
- Compute the absolute difference:
Key Implementation Details:
-
The boundary checking is crucial: for the top-left diagonal, we stop when either index would become negative. For the bottom-right diagonal, we check that the next position would still be within bounds before moving.
-
Sets are used for automatic deduplication - when we add a value that already exists in the set, the set size doesn't increase, giving us the correct count of distinct values.
-
The movement pattern
(x-1, y-1)
for up-left and(x+1, y+1)
for down-right ensures we stay on the same diagonal line throughout the traversal.
Time Complexity: O(m * n * min(m, n))
where the third factor comes from the diagonal traversal which can be at most min(m, n)
cells long.
Space Complexity: O(min(m, n))
for the sets used to track distinct values, plus O(m * n)
for the answer matrix.
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.
Consider a 3x4 grid:
grid = [[1, 2, 3, 4], [5, 1, 2, 3], [6, 5, 1, 2]]
Let's calculate the value for cell (1, 2)
which contains the value 2
:
Step 1: Count distinct values on the top-left diagonal
- Start at position
(1, 2)
withx = 1, y = 2
- Create empty set
s = {}
- Move diagonally up-left:
- First iteration:
x = 0, y = 1
, addgrid[0][1] = 2
to set →s = {2}
- Second iteration would make
x = -1
, so we stop (out of bounds)
- First iteration:
- Count of distinct values:
tl = len({2}) = 1
Step 2: Count distinct values on the bottom-right diagonal
- Reset to position
(1, 2)
withx = 1, y = 2
- Create new empty set
s = {}
- Move diagonally down-right:
- First iteration: Check if
x + 1 < 3
andy + 1 < 4
→ both true - Move to
x = 2, y = 3
, addgrid[2][3] = 2
to set →s = {2}
- Second iteration would make
y = 4
, which is out of bounds, so we stop
- First iteration: Check if
- Count of distinct values:
br = len({2}) = 1
Step 3: Calculate the answer
answer[1][2] = |tl - br| = |1 - 1| = 0
Let's do one more calculation for cell (0, 0)
which contains value 1
:
Top-left diagonal:
- Start at
(0, 0)
- Can't move up-left (already at top-left corner)
tl = 0
(no values found)
Bottom-right diagonal:
- Start at
(0, 0)
- Move to
(1, 1)
→ addgrid[1][1] = 1
→s = {1}
- Move to
(2, 2)
→ addgrid[2][2] = 1
→s = {1}
(still just one distinct value) - Can't move further (would go out of bounds)
br = len({1}) = 1
Result: answer[0][0] = |0 - 1| = 1
The complete answer matrix would be:
answer = [[1, 0, 0, 1], [1, 0, 0, 0], [1, 0, 0, 0]]
This example demonstrates how the algorithm traverses diagonals in both directions, uses sets to track distinct values, and calculates the absolute difference for each cell.
Solution Implementation
1class Solution:
2 def differenceOfDistinctValues(self, grid: List[List[int]]) -> List[List[int]]:
3 """
4 Calculate the absolute difference between distinct values in top-left and bottom-right diagonals
5 for each cell in the grid.
6
7 Args:
8 grid: 2D list of integers
9
10 Returns:
11 2D list where each cell contains the absolute difference of distinct values
12 in its top-left and bottom-right diagonals
13 """
14 rows, cols = len(grid), len(grid[0])
15 result = [[0] * cols for _ in range(rows)]
16
17 # Process each cell in the grid
18 for row in range(rows):
19 for col in range(cols):
20 # Count distinct values in top-left diagonal
21 curr_row, curr_col = row, col
22 top_left_values = set()
23
24 # Move diagonally up-left and collect values
25 while curr_row > 0 and curr_col > 0:
26 curr_row -= 1
27 curr_col -= 1
28 top_left_values.add(grid[curr_row][curr_col])
29
30 top_left_count = len(top_left_values)
31
32 # Count distinct values in bottom-right diagonal
33 curr_row, curr_col = row, col
34 bottom_right_values = set()
35
36 # Move diagonally down-right and collect values
37 while curr_row + 1 < rows and curr_col + 1 < cols:
38 curr_row += 1
39 curr_col += 1
40 bottom_right_values.add(grid[curr_row][curr_col])
41
42 bottom_right_count = len(bottom_right_values)
43
44 # Store the absolute difference
45 result[row][col] = abs(top_left_count - bottom_right_count)
46
47 return result
48
1class Solution {
2 public int[][] differenceOfDistinctValues(int[][] grid) {
3 // Get dimensions of the grid
4 int rows = grid.length;
5 int cols = grid[0].length;
6
7 // Initialize result matrix with same dimensions
8 int[][] result = new int[rows][cols];
9
10 // Iterate through each cell in the grid
11 for (int row = 0; row < rows; row++) {
12 for (int col = 0; col < cols; col++) {
13 // Calculate distinct values in top-left diagonal
14 int currentRow = row;
15 int currentCol = col;
16 Set<Integer> topLeftValues = new HashSet<>();
17
18 // Move diagonally up-left and collect distinct values
19 while (currentRow > 0 && currentCol > 0) {
20 currentRow--;
21 currentCol--;
22 topLeftValues.add(grid[currentRow][currentCol]);
23 }
24 int topLeftCount = topLeftValues.size();
25
26 // Calculate distinct values in bottom-right diagonal
27 currentRow = row;
28 currentCol = col;
29 Set<Integer> bottomRightValues = new HashSet<>();
30
31 // Move diagonally down-right and collect distinct values
32 while (currentRow < rows - 1 && currentCol < cols - 1) {
33 currentRow++;
34 currentCol++;
35 bottomRightValues.add(grid[currentRow][currentCol]);
36 }
37 int bottomRightCount = bottomRightValues.size();
38
39 // Store the absolute difference in the result matrix
40 result[row][col] = Math.abs(topLeftCount - bottomRightCount);
41 }
42 }
43
44 return result;
45 }
46}
47
1class Solution {
2public:
3 vector<vector<int>> differenceOfDistinctValues(vector<vector<int>>& grid) {
4 int rows = grid.size();
5 int cols = grid[0].size();
6
7 // Initialize result matrix with same dimensions as input grid
8 vector<vector<int>> result(rows, vector<int>(cols));
9
10 // Process each cell in the grid
11 for (int row = 0; row < rows; ++row) {
12 for (int col = 0; col < cols; ++col) {
13 // Count distinct values in top-left diagonal
14 int currentRow = row;
15 int currentCol = col;
16 unordered_set<int> distinctValues;
17
18 // Move diagonally up-left and collect distinct values
19 while (currentRow > 0 && currentCol > 0) {
20 distinctValues.insert(grid[--currentRow][--currentCol]);
21 }
22 int topLeftCount = distinctValues.size();
23
24 // Reset position and clear set for bottom-right diagonal
25 currentRow = row;
26 currentCol = col;
27 distinctValues.clear();
28
29 // Move diagonally down-right and collect distinct values
30 while (currentRow < rows - 1 && currentCol < cols - 1) {
31 distinctValues.insert(grid[++currentRow][++currentCol]);
32 }
33 int bottomRightCount = distinctValues.size();
34
35 // Store absolute difference of distinct counts
36 result[row][col] = abs(topLeftCount - bottomRightCount);
37 }
38 }
39
40 return result;
41 }
42};
43
1/**
2 * Calculates the absolute difference between distinct values in top-left and bottom-right diagonals
3 * for each cell in the grid
4 * @param grid - 2D array of numbers
5 * @returns 2D array where each cell contains the absolute difference of distinct values
6 */
7function differenceOfDistinctValues(grid: number[][]): number[][] {
8 const rows: number = grid.length;
9 const cols: number = grid[0].length;
10
11 // Initialize result matrix with zeros
12 const result: number[][] = Array(rows)
13 .fill(0)
14 .map(() => Array(cols).fill(0));
15
16 // Process each cell in the grid
17 for (let row = 0; row < rows; ++row) {
18 for (let col = 0; col < cols; ++col) {
19 // Count distinct values in top-left diagonal
20 let currentRow: number = row;
21 let currentCol: number = col;
22 const topLeftSet = new Set<number>();
23
24 // Move diagonally up-left and collect values
25 while (currentRow > 0 && currentCol > 0) {
26 currentRow--;
27 currentCol--;
28 topLeftSet.add(grid[currentRow][currentCol]);
29 }
30 const topLeftCount: number = topLeftSet.size;
31
32 // Count distinct values in bottom-right diagonal
33 currentRow = row;
34 currentCol = col;
35 const bottomRightSet = new Set<number>();
36
37 // Move diagonally down-right and collect values
38 while (currentRow + 1 < rows && currentCol + 1 < cols) {
39 currentRow++;
40 currentCol++;
41 bottomRightSet.add(grid[currentRow][currentCol]);
42 }
43 const bottomRightCount: number = bottomRightSet.size;
44
45 // Calculate and store the absolute difference
46 result[row][col] = Math.abs(topLeftCount - bottomRightCount);
47 }
48 }
49
50 return result;
51}
52
Time and Space Complexity
Time Complexity: O(m × n × min(m, n))
The code iterates through each cell in the grid using two nested loops, which gives us O(m × n)
iterations. For each cell (i, j)
, it performs two diagonal traversals:
- One traversal going up-left from
(i, j)
to collect distinct values - One traversal going down-right from
(i, j)
to collect distinct values
Each diagonal traversal can go at most min(m, n)
steps since a diagonal is bounded by the smaller dimension of the grid. Although each traversal uses a set to track distinct values (with O(1)
average case for add
operations), the traversal itself takes O(min(m, n))
time.
Therefore, the total time complexity is O(m × n × min(m, n))
.
Space Complexity: O(m × n)
The space complexity consists of:
- The output array
ans
which is am × n
matrix:O(m × n)
- The temporary set
s
used for storing distinct values during diagonal traversals:O(min(m, n))
in the worst case when all diagonal elements are distinct - Other variables like
x
,y
,tl
,br
:O(1)
Since O(m × n)
dominates O(min(m, n))
, the overall space complexity is O(m × n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Error in Diagonal Traversal
A common mistake is incorrectly handling the boundary conditions or including the current cell in the diagonal counts. The problem explicitly states to exclude grid[r][c]
itself when counting distinct values.
Incorrect Implementation:
# Wrong: This includes the current cell in the count while curr_row >= 0 and curr_col >= 0: top_left_values.add(grid[curr_row][curr_col]) curr_row -= 1 curr_col -= 1
Correct Implementation:
# Right: Move first, then add to set (excludes current cell) while curr_row > 0 and curr_col > 0: curr_row -= 1 curr_col -= 1 top_left_values.add(grid[curr_row][curr_col])
2. Reusing the Same Set for Both Diagonals
Another pitfall is trying to optimize by reusing the same set, which leads to incorrect counts since the set would contain values from both diagonals.
Incorrect Implementation:
distinct_values = set()
# Count top-left diagonal
while curr_row > 0 and curr_col > 0:
curr_row -= 1
curr_col -= 1
distinct_values.add(grid[curr_row][curr_col])
top_left_count = len(distinct_values)
# Wrong: Set still contains values from top-left diagonal
curr_row, curr_col = row, col
while curr_row + 1 < rows and curr_col + 1 < cols:
curr_row += 1
curr_col += 1
distinct_values.add(grid[curr_row][curr_col])
bottom_right_count = len(distinct_values) - top_left_count # This is incorrect!
Correct Implementation:
# Create separate sets for each diagonal
top_left_values = set()
# ... collect top-left values ...
bottom_right_values = set() # New set for bottom-right
# ... collect bottom-right values ...
3. Incorrect Boundary Checking Logic
Using the wrong comparison operators or checking conditions after moving can cause index out of bounds errors.
Incorrect Implementation:
# Wrong: This could cause index out of bounds while curr_row >= 0 and curr_col >= 0: curr_row -= 1 curr_col -= 1 if curr_row >= 0 and curr_col >= 0: # Additional check needed top_left_values.add(grid[curr_row][curr_col])
Correct Implementation:
# Right: Check before moving ensures we never go out of bounds while curr_row > 0 and curr_col > 0: curr_row -= 1 curr_col -= 1 top_left_values.add(grid[curr_row][curr_col])
4. Forgetting to Reset Position Variables
When transitioning from counting the top-left diagonal to the bottom-right diagonal, forgetting to reset the position variables will start the second traversal from the wrong position.
Incorrect Implementation:
curr_row, curr_col = row, col # Count top-left diagonal while curr_row > 0 and curr_col > 0: curr_row -= 1 curr_col -= 1 top_left_values.add(grid[curr_row][curr_col]) # Wrong: curr_row and curr_col are now at the end of top-left diagonal # not at the original (row, col) position while curr_row + 1 < rows and curr_col + 1 < cols: curr_row += 1 curr_col += 1 bottom_right_values.add(grid[curr_row][curr_col])
Correct Implementation:
# Reset position for bottom-right diagonal traversal
curr_row, curr_col = row, col
bottom_right_values = set()
while curr_row + 1 < rows and curr_col + 1 < cols:
curr_row += 1
curr_col += 1
bottom_right_values.add(grid[curr_row][curr_col])
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!