Facebook Pixel

2711. Difference of Number of Distinct Values on Diagonals

MediumArrayHash TableMatrix
Leetcode Link

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:

  1. 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, excluding grid[r][c] itself. Call this count leftAbove[r][c].

  2. 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, excluding grid[r][c] itself. Call this count rightBelow[r][c].

  3. 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
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. For each cell in the grid, treat it as our reference point
  2. Walk diagonally up-left from (i, j) by repeatedly doing x = x - 1, y = y - 1 until we hit the grid boundary (when either x becomes 0 or y becomes 0)
  3. Collect all values encountered in a set to get the distinct count
  4. Similarly, walk diagonally down-right from (i, j) by repeatedly doing x = x + 1, y = y + 1 until we hit the grid boundary (when either x reaches m-1 or y reaches n-1)
  5. 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:

  1. Initialize the result matrix: Create an m x n matrix ans filled with zeros to store our answers.

  2. Iterate through each cell: Use nested loops to visit each cell (i, j) in the grid.

  3. Count distinct values on the top-left diagonal:

    • Start from the current position (i, j) by setting x = 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])
    • The loop continues while both x > 0 and y > 0 (since x and y evaluates to True only when both are non-zero)
    • Store the count of distinct values: tl = len(s)
  4. 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])
    • The loop continues while we can move without going out of bounds
    • Store the count of distinct values: br = len(s)
  5. Calculate and store the result:

    • Compute the absolute difference: ans[i][j] = abs(tl - br)

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 Evaluator

Example 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) with x = 1, y = 2
  • Create empty set s = {}
  • Move diagonally up-left:
    • First iteration: x = 0, y = 1, add grid[0][1] = 2 to set → s = {2}
    • Second iteration would make x = -1, so we stop (out of bounds)
  • Count of distinct values: tl = len({2}) = 1

Step 2: Count distinct values on the bottom-right diagonal

  • Reset to position (1, 2) with x = 1, y = 2
  • Create new empty set s = {}
  • Move diagonally down-right:
    • First iteration: Check if x + 1 < 3 and y + 1 < 4 → both true
    • Move to x = 2, y = 3, add grid[2][3] = 2 to set → s = {2}
    • Second iteration would make y = 4, which is out of bounds, so we stop
  • 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) → add grid[1][1] = 1s = {1}
  • Move to (2, 2) → add grid[2][2] = 1s = {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 a m × 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])
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More