1351. Count Negative Numbers in a Sorted Matrix
Problem Description
You are given a m x n matrix grid where all elements are sorted in non-increasing order both row-wise and column-wise. This means:
- Each row is sorted from left to right in descending order (or non-increasing order)
- Each column is sorted from top to bottom in descending order (or non-increasing order)
Your task is to count and return the total number of negative numbers present in the entire matrix.
For example, if you have a matrix like:
[[ 4, 3, 2, -1], [ 3, 2, 1, -1], [ 1, 1, -1, -2], [-1, -1, -2, -3]]
Each row decreases from left to right, and each column decreases from top to bottom. The negative numbers in this matrix are: -1, -1, -1, -2, -1, -1, -2, -3, giving us a total count of 8.
The solution uses binary search on each row to find the first negative number. Since each row is sorted in non-increasing order, we can define a feasible function: feasible(mid) = grid[row][mid] < 0. This creates a pattern of [false, false, ..., true, true] where we search for the first true (first negative). Once found at index firstNegIndex, all elements from firstNegIndex to n-1 are negative, giving us n - firstNegIndex negatives for that row.
This approach efficiently counts all negatives in O(m log n) time complexity by performing binary search on each of the m rows.
Intuition
The key insight comes from observing the sorted property of each row. Since each row is sorted in non-increasing order (descending), the elements follow a pattern: all non-negative numbers come first, followed by all negative numbers.
This creates a boolean pattern when we ask "is this element negative?":
[false, false, false, true, true, true]
This is exactly the pattern that binary search excels at: finding the first true in a sorted boolean array. We define our feasible function as:
feasible(mid) = grid[row][mid] < 0
For each row, we use binary search to find the first negative index (firstTrueIndex). Once we find this boundary:
- If
firstTrueIndexis found (not -1), all elements from indexfirstTrueIndexton-1are negative - The count for this row is
n - firstTrueIndex - If no negative exists in the row (
firstTrueIndex == -1), contribute 0
By applying binary search to each of the m rows, we efficiently count all negatives without checking every element.
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def countNegatives(self, grid: List[List[int]]) -> int:
3 """
4 Count the number of negative numbers in a sorted matrix.
5 Uses binary search on each row to find the first negative.
6
7 Time Complexity: O(m log n) where m is rows and n is columns
8 Space Complexity: O(1)
9 """
10 rows, cols = len(grid), len(grid[0])
11 total_count = 0
12
13 # Apply binary search on each row
14 for row in range(rows):
15 # Binary search template: find first negative (first true)
16 left, right = 0, cols - 1
17 first_true_index = -1
18
19 while left <= right:
20 mid = (left + right) // 2
21 # Feasible condition: is this element negative?
22 if grid[row][mid] < 0:
23 first_true_index = mid
24 right = mid - 1 # Search left for earlier negative
25 else:
26 left = mid + 1 # Search right for negatives
27
28 # If we found a negative, count all elements from that index to end
29 if first_true_index != -1:
30 total_count += cols - first_true_index
31
32 return total_count
331class Solution {
2 /**
3 * Count negative numbers in a sorted matrix.
4 * Uses binary search on each row to find the first negative.
5 *
6 * Time Complexity: O(m log n) where m is rows and n is columns
7 * Space Complexity: O(1)
8 */
9 public int countNegatives(int[][] grid) {
10 int rows = grid.length;
11 int cols = grid[0].length;
12 int totalCount = 0;
13
14 // Apply binary search on each row
15 for (int row = 0; row < rows; row++) {
16 // Binary search template: find first negative (first true)
17 int left = 0;
18 int right = cols - 1;
19 int firstTrueIndex = -1;
20
21 while (left <= right) {
22 int mid = left + (right - left) / 2;
23 // Feasible condition: is this element negative?
24 if (grid[row][mid] < 0) {
25 firstTrueIndex = mid;
26 right = mid - 1; // Search left for earlier negative
27 } else {
28 left = mid + 1; // Search right for negatives
29 }
30 }
31
32 // If we found a negative, count all elements from that index to end
33 if (firstTrueIndex != -1) {
34 totalCount += cols - firstTrueIndex;
35 }
36 }
37
38 return totalCount;
39 }
40}
411class Solution {
2public:
3 /**
4 * Count negative numbers in a sorted matrix.
5 * Uses binary search on each row to find the first negative.
6 *
7 * Time Complexity: O(m log n) where m is rows and n is columns
8 * Space Complexity: O(1)
9 */
10 int countNegatives(vector<vector<int>>& grid) {
11 int rows = grid.size();
12 int cols = grid[0].size();
13 int totalCount = 0;
14
15 // Apply binary search on each row
16 for (int row = 0; row < rows; row++) {
17 // Binary search template: find first negative (first true)
18 int left = 0;
19 int right = cols - 1;
20 int firstTrueIndex = -1;
21
22 while (left <= right) {
23 int mid = left + (right - left) / 2;
24 // Feasible condition: is this element negative?
25 if (grid[row][mid] < 0) {
26 firstTrueIndex = mid;
27 right = mid - 1; // Search left for earlier negative
28 } else {
29 left = mid + 1; // Search right for negatives
30 }
31 }
32
33 // If we found a negative, count all elements from that index to end
34 if (firstTrueIndex != -1) {
35 totalCount += cols - firstTrueIndex;
36 }
37 }
38
39 return totalCount;
40 }
41};
421/**
2 * Counts the number of negative numbers in a sorted matrix.
3 * Uses binary search on each row to find the first negative.
4 *
5 * Time Complexity: O(m log n) where m is rows and n is columns
6 * Space Complexity: O(1)
7 */
8function countNegatives(grid: number[][]): number {
9 const rows: number = grid.length;
10 const cols: number = grid[0].length;
11 let totalCount: number = 0;
12
13 // Apply binary search on each row
14 for (let row = 0; row < rows; row++) {
15 // Binary search template: find first negative (first true)
16 let left: number = 0;
17 let right: number = cols - 1;
18 let firstTrueIndex: number = -1;
19
20 while (left <= right) {
21 const mid: number = Math.floor((left + right) / 2);
22 // Feasible condition: is this element negative?
23 if (grid[row][mid] < 0) {
24 firstTrueIndex = mid;
25 right = mid - 1; // Search left for earlier negative
26 } else {
27 left = mid + 1; // Search right for negatives
28 }
29 }
30
31 // If we found a negative, count all elements from that index to end
32 if (firstTrueIndex !== -1) {
33 totalCount += cols - firstTrueIndex;
34 }
35 }
36
37 return totalCount;
38}
39Solution Approach
The implementation uses binary search on each row to find the first negative number, following the exact boundary-finding template.
Algorithm Steps:
-
Initialize variables:
rows, cols = len(grid), len(grid[0])- Get matrix dimensionstotalCount = 0- Counter for negative numbers
-
For each row, apply binary search:
- Initialize:
left = 0,right = cols - 1,firstTrueIndex = -1 - Define feasible condition:
grid[row][mid] < 0(is this position negative?)
- Initialize:
-
Binary search loop (
while left <= right):- Calculate
mid = (left + right) // 2 - If
grid[row][mid] < 0: Found a negative (feasible is true)- Record this position:
firstTrueIndex = mid - Search left for earlier negative:
right = mid - 1
- Record this position:
- Else: Current element is non-negative
- Search right for negatives:
left = mid + 1
- Search right for negatives:
- Calculate
-
Count negatives for this row:
- If
firstTrueIndex != -1: Addcols - firstTrueIndexto total count - If
firstTrueIndex == -1: No negatives in this row, add 0
- If
-
Return the total count after processing all rows
Why this works:
Each row is sorted in non-increasing order, creating the pattern [non-neg, non-neg, ..., neg, neg]. Binary search finds the boundary (first negative) in O(log n) time. Once we know where negatives start, we count all remaining elements in that row.
Time Complexity: O(m log n) - Binary search O(log n) on each of m rows
Space Complexity: O(1) - Only using a few variables for tracking
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 trace through a small example using the binary search template:
grid = [[3, 2, -1], [2, 0, -2], [1, -1, -3]]
Initial Setup:
rows = 3, cols = 3(3x3 matrix)totalCount = 0
Row 0: [3, 2, -1]
- Initialize:
left = 0, right = 2, firstTrueIndex = -1 - Iteration 1:
mid = 1, grid[0][1] = 2 ≥ 0 (not negative) →left = 2 - Iteration 2:
mid = 2, grid[0][2] = -1 < 0 (negative!) →firstTrueIndex = 2,right = 1 - Loop ends:
left = 2 > right = 1 - Result:
firstTrueIndex = 2, addcols - 2 = 1negative totalCount = 1
Row 1: [2, 0, -2]
- Initialize:
left = 0, right = 2, firstTrueIndex = -1 - Iteration 1:
mid = 1, grid[1][1] = 0 ≥ 0 (not negative) →left = 2 - Iteration 2:
mid = 2, grid[1][2] = -2 < 0 (negative!) →firstTrueIndex = 2,right = 1 - Loop ends:
left = 2 > right = 1 - Result:
firstTrueIndex = 2, addcols - 2 = 1negative totalCount = 2
Row 2: [1, -1, -3]
- Initialize:
left = 0, right = 2, firstTrueIndex = -1 - Iteration 1:
mid = 1, grid[2][1] = -1 < 0 (negative!) →firstTrueIndex = 1,right = 0 - Iteration 2:
mid = 0, grid[2][0] = 1 ≥ 0 (not negative) →left = 1 - Loop ends:
left = 1 > right = 0 - Result:
firstTrueIndex = 1, addcols - 1 = 2negatives totalCount = 4
Final Result: Total negative count = 4
The binary search found the first negative in each row: index 2, index 2, and index 1. By counting all elements from that index to the end of each row, we efficiently found all 4 negative numbers (-1, -2, -1, -3).
Time and Space Complexity
Time Complexity: O(m log n), where m is the number of rows and n is the number of columns in the grid.
The algorithm performs binary search on each of the m rows. Each binary search takes O(log n) time since we're searching through n columns. Therefore, the total time is O(m × log n) = O(m log n).
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space for variables like left, right, firstTrueIndex, and counters. No additional data structures are created that scale with the input dimensions.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using the Wrong Binary Search Template
Pitfall: Using while left < right with right = mid instead of the correct template with while left <= right and right = mid - 1.
Why it fails: Different binary search variants have different behaviors. Using the wrong template can cause off-by-one errors or infinite loops.
Solution: Always use the boundary-finding template with firstTrueIndex:
# Correct template left, right = 0, cols - 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if grid[row][mid] < 0: first_true_index = mid right = mid - 1 else: left = mid + 1
2. Returning left Instead of Tracking firstTrueIndex
Pitfall: Some solutions try to return left directly after the loop, which may point to an invalid index if no negative exists.
Why it fails: When there are no negatives in a row, left will equal cols (out of bounds), and using it directly would add incorrect counts.
Solution: Track firstTrueIndex explicitly and check if it's -1:
if first_true_index != -1: total_count += cols - first_true_index # Otherwise, no negatives in this row, contribute 0
3. Incorrect Count Calculation
Pitfall: Counting cols - firstTrueIndex - 1 or cols - firstTrueIndex + 1 instead of cols - firstTrueIndex.
Why it fails:
- Using
cols - firstTrueIndex - 1would not count the first negative itself - Using
cols - firstTrueIndex + 1would count an extra non-existent element
Solution: When firstTrueIndex is the index of the first negative, there are exactly cols - firstTrueIndex elements from that index to the end of the row (inclusive):
# If firstTrueIndex = 2 and cols = 5 # Elements at indices 2, 3, 4 are negative = 5 - 2 = 3 elements total_count += cols - first_true_index
4. Not Resetting Variables for Each Row
Pitfall: Forgetting to reset left, right, and firstTrueIndex for each row's binary search.
Why it fails: Using stale values from the previous row's search will produce incorrect results.
Solution: Initialize all binary search variables inside the row loop:
for row in range(rows):
left, right = 0, cols - 1 # Reset for each row
first_true_index = -1 # Reset for each row
# ... binary search ...
5. Handling Edge Cases
Pitfall: Not considering edge cases like all positive elements or all negative elements in a row.
Why it fails: The code might return incorrect counts.
Solution: The template handles these naturally:
# All positive row: firstTrueIndex stays -1, contributes 0 # All negative row: firstTrueIndex = 0, contributes cols - 0 = cols # Single element row: binary search works correctly with left = right = 0
What's the output of running the following function using the following tree as input?

1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
121import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
181function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!