{x}
blog image

Count Negative Numbers in a Sorted Matrix

Given a m x n matrix grid which is sorted in non-increasing order both row-wise and column-wise, return the number of negative numbers in grid.

 

Example 1:

Input: grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
Output: 8
Explanation: There are 8 negatives number in the matrix.

Example 2:

Input: grid = [[3,2],[1,0]]
Output: 0

 

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 100
  • -100 <= grid[i][j] <= 100

 

Follow up: Could you find an O(n + m) solution?

Solution Explanation for Counting Negative Numbers in a Sorted Matrix

This problem involves counting negative numbers within a matrix where both rows and columns are sorted in non-increasing order. Two efficient approaches exist: one with linear time complexity O(m+n) and another with O(m*log n) complexity.

Approach 1: Linear Traversal (O(m+n) Time Complexity)

This approach leverages the sorted nature of the matrix to efficiently count negative numbers. We start from the bottom-left corner (or top-right; the choice is symmetrical) of the matrix.

Algorithm:

  1. Initialization: Initialize a count ans to 0, and start at the bottom-left corner (i = m-1, j = 0).
  2. Iteration: While i is within bounds and j is within bounds:
    • If grid[i][j] < 0: This means all elements to the right in this row are also negative. Add n - j (the number of remaining elements in the row) to ans, and move up one row (i--).
    • Otherwise (grid[i][j] >= 0): Move to the right one column (j++).
  3. Return: Return ans.

Why this works: Because the matrix is sorted, if an element is non-negative, all elements to its left in the same row are also non-negative. Similarly, if an element is negative, all elements below it in the same column are also negative. This property allows us to efficiently skip sections of the matrix.

Time Complexity: O(m+n) because in the worst case, we traverse at most m rows and n columns.

Space Complexity: O(1) because we use only a constant amount of extra space.

Approach 2: Binary Search (O(m*log n) Time Complexity)

This approach performs a binary search on each row to find the index of the first negative number.

Algorithm:

  1. Initialization: Initialize a count ans to 0.
  2. Row Iteration: Iterate through each row of the matrix.
  3. Binary Search: For each row, perform a binary search to find the index of the first negative number. The bisect_left function (or equivalent) is efficient for this purpose.
  4. Count: The number of negative numbers in the row is the total number of columns minus the index of the first negative number (if found). Add this count to ans.
  5. Return: Return ans.

Time Complexity: O(m * log n) because we perform a binary search (log n time) on each of the m rows.

Space Complexity: O(1).

Code Examples (Python):

Approach 1 (Linear Traversal):

def countNegatives(grid):
    m, n = len(grid), len(grid[0])
    i, j = m - 1, 0
    ans = 0
    while i >= 0 and j < n:
        if grid[i][j] < 0:
            ans += n - j
            i -= 1
        else:
            j += 1
    return ans

Approach 2 (Binary Search):

import bisect
 
def countNegatives(grid):
    return sum(len(row) - bisect.bisect_left(row, 0) for row in grid)
 

The code in other languages (Java, C++, Go, JavaScript, TypeScript, Rust) follows similar logic, adapting the syntax and libraries as needed. The choice between these approaches depends on the specific constraints and performance requirements. If the matrix is very large, the linear traversal (Approach 1) might be slightly faster, while for smaller matrices, the difference may be negligible.