3128. Right Triangles
Problem Description
You have a 2D boolean matrix grid
filled with 0s and 1s.
A right triangle is formed by selecting 3 elements from the grid that all have value 1, where:
- One element serves as the right angle vertex
- One element is in the same row as the right angle vertex
- One element is in the same column as the right angle vertex
- The 3 elements don't need to be adjacent to each other
Your task is to count how many such right triangles can be formed using the 1s in the grid.
For example, if you have a 1 at position (i, j)
that serves as the right angle, you can form a right triangle with any other 1 in row i
and any other 1 in column j
. If there are r
total 1s in row i
and c
total 1s in column j
, then this position can form (r-1) × (c-1)
right triangles (excluding the position itself from both counts).
The solution counts the number of 1s in each row and column first, then for each 1 in the grid, calculates how many right triangles can be formed with that 1 as the right angle vertex.
Intuition
The key insight is recognizing that every right triangle in the grid must have one vertex serving as the "corner" or right angle, with the other two vertices sharing either a row or column with this corner vertex.
Instead of checking all possible combinations of three 1s in the grid (which would be computationally expensive), we can think about it from the perspective of each potential right angle vertex. For any 1 in the grid at position (i, j)
, it can serve as the right angle of a triangle if:
- There's at least one other 1 in the same row
i
- There's at least one other 1 in the same column
j
If we know how many 1s are in row i
and column j
, we can use the multiplication principle from combinatorics. If there are rows[i]
total 1s in row i
and cols[j]
total 1s in column j
, then:
- We have
rows[i] - 1
choices for the horizontal vertex (excluding the corner itself) - We have
cols[j] - 1
choices for the vertical vertex (excluding the corner itself) - Total triangles with this corner =
(rows[i] - 1) × (cols[j] - 1)
This leads us to a two-pass approach:
- First pass: Count the number of 1s in each row and column
- Second pass: For each 1, calculate how many triangles it can form as a right angle vertex
This approach is efficient because we only need to traverse the grid twice, giving us O(m × n)
time complexity, where m
and n
are the dimensions of the grid.
Learn more about Math and Combinatorics patterns.
Solution Approach
The implementation follows a counting and enumeration strategy:
Step 1: Count 1s in each row and column
We initialize two arrays:
rows
: An array of sizelen(grid)
to store the count of 1s in each rowcols
: An array of sizelen(grid[0])
to store the count of 1s in each column
We traverse the entire grid once, and for each cell (i, j)
that contains a 1, we increment both rows[i]
and cols[j]
:
for i, row in enumerate(grid):
for j, x in enumerate(row):
rows[i] += x # x is either 0 or 1
cols[j] += x
Step 2: Calculate the number of right triangles
We traverse the grid again, and for each cell (i, j)
that contains a 1, we treat it as the right angle vertex of potential triangles:
ans = 0
for i, row in enumerate(grid):
for j, x in enumerate(row):
if x: # if current cell is 1
ans += (rows[i] - 1) * (cols[j] - 1)
The formula (rows[i] - 1) * (cols[j] - 1)
calculates:
rows[i] - 1
: Number of other 1s in the same row (excluding the current cell)cols[j] - 1
: Number of other 1s in the same column (excluding the current cell)- Their product gives us the total number of valid right triangles with the current cell as the right angle
Time Complexity: O(m × n)
where m
and n
are the dimensions of the grid, as we traverse the grid twice.
Space Complexity: O(m + n)
for storing the row and column counts.
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 this 3×4 grid:
grid = [[0, 1, 0, 1], [1, 1, 1, 0], [1, 0, 0, 1]]
Step 1: Count 1s in each row and column
First, we count how many 1s are in each row and column:
-
Row 0: Contains 1s at positions (0,1) and (0,3) →
rows[0] = 2
-
Row 1: Contains 1s at positions (1,0), (1,1), and (1,2) →
rows[1] = 3
-
Row 2: Contains 1s at positions (2,0) and (2,3) →
rows[2] = 2
-
Column 0: Contains 1s at positions (1,0) and (2,0) →
cols[0] = 2
-
Column 1: Contains 1s at positions (0,1) and (1,1) →
cols[1] = 2
-
Column 2: Contains 1s at position (1,2) →
cols[2] = 1
-
Column 3: Contains 1s at positions (0,3) and (2,3) →
cols[3] = 2
So we have: rows = [2, 3, 2]
and cols = [2, 2, 1, 2]
Step 2: Calculate triangles for each 1 as the right angle vertex
Now we visit each 1 in the grid and calculate how many triangles it can form:
-
Position (0,1):
(rows[0]-1) × (cols[1]-1) = (2-1) × (2-1) = 1 × 1 = 1
triangle- This forms a triangle with vertices at (0,1), (0,3), and (1,1)
-
Position (0,3):
(rows[0]-1) × (cols[3]-1) = (2-1) × (2-1) = 1 × 1 = 1
triangle- This forms a triangle with vertices at (0,3), (0,1), and (2,3)
-
Position (1,0):
(rows[1]-1) × (cols[0]-1) = (3-1) × (2-1) = 2 × 1 = 2
triangles- Triangle 1: vertices at (1,0), (1,1), and (2,0)
- Triangle 2: vertices at (1,0), (1,2), and (2,0)
-
Position (1,1):
(rows[1]-1) × (cols[1]-1) = (3-1) × (2-1) = 2 × 1 = 2
triangles- Triangle 1: vertices at (1,1), (1,0), and (0,1)
- Triangle 2: vertices at (1,1), (1,2), and (0,1)
-
Position (1,2):
(rows[1]-1) × (cols[2]-1) = (3-1) × (1-1) = 2 × 0 = 0
triangles- No triangles possible (no other 1s in column 2)
-
Position (2,0):
(rows[2]-1) × (cols[0]-1) = (2-1) × (2-1) = 1 × 1 = 1
triangle- This forms a triangle with vertices at (2,0), (2,3), and (1,0)
-
Position (2,3):
(rows[2]-1) × (cols[3]-1) = (2-1) × (2-1) = 1 × 1 = 1
triangle- This forms a triangle with vertices at (2,3), (2,0), and (0,3)
Total triangles: 1 + 1 + 2 + 2 + 0 + 1 + 1 = 8
The algorithm efficiently counts all possible right triangles by treating each 1 as a potential right angle vertex and using the multiplication principle to count valid combinations.
Solution Implementation
1class Solution:
2 def numberOfRightTriangles(self, grid: List[List[int]]) -> int:
3 # Get dimensions of the grid
4 num_rows = len(grid)
5 num_cols = len(grid[0])
6
7 # Count the number of 1s in each row and column
8 row_counts = [0] * num_rows
9 col_counts = [0] * num_cols
10
11 # First pass: calculate the count of 1s in each row and column
12 for row_idx in range(num_rows):
13 for col_idx in range(num_cols):
14 if grid[row_idx][col_idx] == 1:
15 row_counts[row_idx] += 1
16 col_counts[col_idx] += 1
17
18 # Second pass: count right triangles with each 1 as the corner vertex
19 triangle_count = 0
20 for row_idx in range(num_rows):
21 for col_idx in range(num_cols):
22 # If current cell contains a 1, it can be a corner of right triangles
23 if grid[row_idx][col_idx] == 1:
24 # Number of right triangles = (other 1s in same row) * (other 1s in same column)
25 # Subtract 1 from each count to exclude the current cell itself
26 triangle_count += (row_counts[row_idx] - 1) * (col_counts[col_idx] - 1)
27
28 return triangle_count
29
1class Solution {
2 /**
3 * Counts the number of right triangles that can be formed in a binary grid.
4 * A right triangle is formed by three cells with value 1, where one cell serves
5 * as the right angle vertex, sharing its row with one other cell and its column
6 * with the third cell.
7 *
8 * @param grid A 2D binary array where 1 represents a point and 0 represents empty space
9 * @return The total number of right triangles that can be formed
10 */
11 public long numberOfRightTriangles(int[][] grid) {
12 // Get grid dimensions
13 int rowCount = grid.length;
14 int colCount = grid[0].length;
15
16 // Arrays to store the count of 1s in each row and column
17 int[] rowOnesCount = new int[rowCount];
18 int[] colOnesCount = new int[colCount];
19
20 // Count the number of 1s in each row and column
21 for (int row = 0; row < rowCount; ++row) {
22 for (int col = 0; col < colCount; ++col) {
23 rowOnesCount[row] += grid[row][col];
24 colOnesCount[col] += grid[row][col];
25 }
26 }
27
28 // Calculate the number of right triangles
29 long totalTriangles = 0;
30
31 // For each cell with value 1, it can be the right angle vertex
32 for (int row = 0; row < rowCount; ++row) {
33 for (int col = 0; col < colCount; ++col) {
34 if (grid[row][col] == 1) {
35 // Number of triangles with this cell as the right angle vertex
36 // = (other 1s in the same row) * (other 1s in the same column)
37 totalTriangles += (rowOnesCount[row] - 1) * (colOnesCount[col] - 1);
38 }
39 }
40 }
41
42 return totalTriangles;
43 }
44}
45
1class Solution {
2public:
3 long long numberOfRightTriangles(vector<vector<int>>& grid) {
4 // Get dimensions of the grid
5 int numRows = grid.size();
6 int numCols = grid[0].size();
7
8 // Arrays to store count of 1s in each row and column
9 vector<int> rowOnesCount(numRows);
10 vector<int> colOnesCount(numCols);
11
12 // First pass: Count the number of 1s in each row and column
13 for (int row = 0; row < numRows; ++row) {
14 for (int col = 0; col < numCols; ++col) {
15 rowOnesCount[row] += grid[row][col];
16 colOnesCount[col] += grid[row][col];
17 }
18 }
19
20 // Variable to store the total number of right triangles
21 long long totalTriangles = 0;
22
23 // Second pass: For each cell with value 1, calculate the number of right triangles
24 // where this cell is the vertex of the right angle
25 for (int row = 0; row < numRows; ++row) {
26 for (int col = 0; col < numCols; ++col) {
27 if (grid[row][col] == 1) {
28 // Number of right triangles = (other 1s in same row) * (other 1s in same column)
29 // We subtract 1 to exclude the current cell itself
30 totalTriangles += static_cast<long long>(rowOnesCount[row] - 1) * (colOnesCount[col] - 1);
31 }
32 }
33 }
34
35 return totalTriangles;
36 }
37};
38
1/**
2 * Counts the number of right triangles that can be formed in a binary grid
3 * where each triangle has its right angle at a cell with value 1
4 * @param grid - 2D binary array where 1s represent points
5 * @returns The total number of right triangles
6 */
7function numberOfRightTriangles(grid: number[][]): number {
8 const rowCount: number = grid.length;
9 const columnCount: number = grid[0].length;
10
11 // Arrays to store the count of 1s in each row and column
12 const onesPerRow: number[] = Array(rowCount).fill(0);
13 const onesPerColumn: number[] = Array(columnCount).fill(0);
14
15 // Count the number of 1s in each row and column
16 for (let row = 0; row < rowCount; ++row) {
17 for (let col = 0; col < columnCount; ++col) {
18 onesPerRow[row] += grid[row][col];
19 onesPerColumn[col] += grid[row][col];
20 }
21 }
22
23 let triangleCount: number = 0;
24
25 // For each cell with value 1, calculate possible right triangles
26 // with this cell as the right angle vertex
27 for (let row = 0; row < rowCount; ++row) {
28 for (let col = 0; col < columnCount; ++col) {
29 if (grid[row][col] === 1) {
30 // Number of triangles = (other 1s in same row) * (other 1s in same column)
31 // Subtract 1 from each count to exclude the current cell
32 triangleCount += (onesPerRow[row] - 1) * (onesPerColumn[col] - 1);
33 }
34 }
35 }
36
37 return triangleCount;
38}
39
Time and Space Complexity
Time Complexity: O(m × n)
The algorithm consists of two main parts:
- First nested loop: Iterates through all
m × n
elements in the grid to count the number of 1s in each row and column. This takesO(m × n)
time. - Second nested loop: Again iterates through all
m × n
elements to calculate the number of right triangles. For each cell with value 1, it performs a constant-time calculation(rows[i] - 1) * (cols[j] - 1)
. This also takesO(m × n)
time.
Total time complexity: O(m × n) + O(m × n) = O(m × n)
Space Complexity: O(m + n)
The algorithm uses:
rows
array of sizem
to store the count of 1s in each row:O(m)
spacecols
array of sizen
to store the count of 1s in each column:O(n)
space- A few scalar variables (
ans
, loop indices) which takeO(1)
space
Total space complexity: O(m) + O(n) = O(m + n)
Where m
is the number of rows and n
is the number of columns in the matrix.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to exclude the vertex cell from counts
A common mistake is using rows[i] * cols[j]
instead of (rows[i] - 1) * (cols[j] - 1)
. This would incorrectly count the vertex cell itself as part of the triangle's other two vertices.
Incorrect:
if grid[row_idx][col_idx] == 1: triangle_count += row_counts[row_idx] * col_counts[col_idx] # Wrong!
Correct:
if grid[row_idx][col_idx] == 1: triangle_count += (row_counts[row_idx] - 1) * (col_counts[col_idx] - 1)
2. Not handling edge cases with insufficient 1s
When a row or column has only one 1 (the vertex itself), the formula (rows[i] - 1) * (cols[j] - 1)
correctly evaluates to 0, but some might try to add explicit checks that are unnecessary and can introduce bugs:
Overcomplicated (and potentially buggy):
if grid[row_idx][col_idx] == 1: if row_counts[row_idx] > 1 and col_counts[col_idx] > 1: triangle_count += (row_counts[row_idx] - 1) * (col_counts[col_idx] - 1) # This check is redundant and might lead to errors if modified incorrectly
Better (simpler and cleaner):
if grid[row_idx][col_idx] == 1: triangle_count += (row_counts[row_idx] - 1) * (col_counts[col_idx] - 1) # Naturally handles the edge case since (0 * n) or (n * 0) = 0
3. Attempting to optimize with a single pass
Some might try to calculate triangles while counting, but this leads to incorrect results because you need complete row/column counts before calculating triangles:
Incorrect single-pass attempt:
triangle_count = 0
row_counts = [0] * num_rows
col_counts = [0] * num_cols
for row_idx in range(num_rows):
for col_idx in range(num_cols):
if grid[row_idx][col_idx] == 1:
# This calculation uses incomplete counts!
triangle_count += row_counts[row_idx] * col_counts[col_idx]
row_counts[row_idx] += 1
col_counts[col_idx] += 1
This would miss triangles because the counts are built incrementally and aren't complete when used.
4. Integer overflow in other languages
While Python handles large integers automatically, in languages like Java or C++, the multiplication (rows[i] - 1) * (cols[j] - 1)
could overflow for large grids. Consider using appropriate data types:
For Java/C++:
// Use long long in C++ or long in Java for large grids long long triangle_count = 0; triangle_count += (long long)(row_counts[i] - 1) * (col_counts[j] - 1);
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
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
Want a Structured Path to Master System Design Too? Don’t Miss This!