2133. Check if Every Row and Column Contains All Numbers
Problem Description
You are given an n x n
integer matrix and need to determine if it's valid according to a specific rule.
A matrix is considered valid if:
- Every row contains all integers from
1
ton
(inclusive) - Every column contains all integers from
1
ton
(inclusive)
In other words, each row must have exactly the numbers 1, 2, 3, ..., n
with no duplicates or missing values, and the same requirement applies to each column.
For example, if n = 3
, then each row should contain exactly the numbers 1, 2, 3
in some order, and each column should also contain exactly the numbers 1, 2, 3
in some order.
The task is to check whether the given matrix satisfies these conditions and return true
if it's valid, or false
otherwise.
The solution uses a clever approach with Python's set
data structure and the zip(*matrix)
operation to transpose the matrix. By checking that each row has exactly n
unique elements (using len(set(row)) == n
), we ensure all numbers from 1
to n
are present without duplicates. The chain
function combines the original rows with the transposed columns, allowing us to check both rows and columns in a single operation.
Intuition
The key insight is recognizing that if a row or column contains all integers from 1
to n
, it must have exactly n
distinct values. Since we're working with exactly n
positions and need exactly the numbers 1
through n
, there's no room for duplicates or missing values.
Think about it this way: if we have n
slots and need to place n
specific numbers (1
to n
), the only way this works is if each number appears exactly once. If any number appears twice, another number must be missing. If any number is outside the range [1, n]
, then some required number is missing.
This leads us to a simple validation strategy: convert each row and column to a set
and check if its size equals n
. A set
automatically removes duplicates, so if len(set(row)) == n
, we know:
- There are no duplicates (otherwise the set would be smaller)
- All numbers must be in the range
[1, n]
(otherwise we couldn't have exactlyn
distinct values in ann
-length row)
The elegant part of the solution is using zip(*matrix)
to transpose the matrix, which turns columns into rows. This allows us to treat columns the same way we treat rows. By using chain(matrix, zip(*matrix))
, we create a single iterable containing all rows followed by all columns, enabling us to check everything in one pass with the all()
function.
This approach avoids explicitly checking if each number from 1
to n
exists, relying instead on the mathematical property that n
distinct values in n
positions with the constraint that values must be from 1
to n
guarantees a valid configuration.
Solution Approach
The solution uses a hash table (implemented as Python's set
) to track unique elements in each row and column. Here's how the implementation works:
Step 1: Get the matrix dimension
- Extract
n = len(matrix)
to know the expected size and range of values
Step 2: Transform columns into rows
- Use
zip(*matrix)
to transpose the matrix - This operation takes the i-th element from each row and groups them into a new tuple, effectively turning columns into rows
- For example, if matrix is
[[1,2],[3,4]]
, thenzip(*matrix)
produces[(1,3), (2,4)]
Step 3: Combine rows and columns
- Use
chain(matrix, zip(*matrix))
to create a single iterable containing all original rows followed by all transposed columns - This allows us to process both rows and columns with the same logic
Step 4: Validate each row and column
- For each row (or transposed column) in the chained iterable:
- Convert it to a
set
to get unique elements - Check if
len(set(row)) == n
- If the set has exactly
n
elements, it means all numbers from1
ton
are present without duplicates
- Convert it to a
Step 5: Return the result
- Use the
all()
function to ensure every row and column passes the validation - If any row or column has
len(set(row)) != n
, theall()
function returnsFalse
- Only if all rows and columns have exactly
n
unique elements does it returnTrue
The hash table approach is efficient because:
- Creating a set from a row/column takes O(n) time
- We check 2n sequences (n rows + n columns)
- Total time complexity: O(n²)
- Space complexity: O(n) for the temporary sets created
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 with a 3×3 matrix to illustrate the solution approach.
Example Matrix:
matrix = [[1, 2, 3], [3, 1, 2], [2, 3, 1]]
Step 1: Get matrix dimension
n = len(matrix) = 3
- We expect each row and column to contain exactly the numbers 1, 2, and 3
Step 2: Transform columns into rows using zip(*matrix)
zip(*matrix)
transposes the matrix:- Column 0:
(1, 3, 2)
- takes first element from each row - Column 1:
(2, 1, 3)
- takes second element from each row - Column 2:
(3, 2, 1)
- takes third element from each row
- Column 0:
Step 3: Combine rows and columns with chain
chain(matrix, zip(*matrix))
creates:[ [1, 2, 3], # Row 0 [3, 1, 2], # Row 1 [2, 3, 1], # Row 2 (1, 3, 2), # Column 0 (transposed) (2, 1, 3), # Column 1 (transposed) (3, 2, 1) # Column 2 (transposed) ]
Step 4: Validate each sequence
- Row 0:
set([1, 2, 3])
={1, 2, 3}
, length = 3 ✓ - Row 1:
set([3, 1, 2])
={1, 2, 3}
, length = 3 ✓ - Row 2:
set([2, 3, 1])
={1, 2, 3}
, length = 3 ✓ - Column 0:
set((1, 3, 2))
={1, 2, 3}
, length = 3 ✓ - Column 1:
set((2, 1, 3))
={1, 2, 3}
, length = 3 ✓ - Column 2:
set((3, 2, 1))
={1, 2, 3}
, length = 3 ✓
Step 5: Return result
- Since all 6 checks pass (
len(set(row)) == 3
for each),all()
returnsTrue
- The matrix is valid!
Counter-example to show failure:
matrix = [[1, 2, 3], [2, 2, 1], # Invalid: has duplicate 2, missing 3 [3, 1, 2]]
- Row 1:
set([2, 2, 1])
={1, 2}
, length = 2 ≠ 3 ✗ - The
all()
function would returnFalse
immediately upon checking this row
Solution Implementation
1from typing import List
2from itertools import chain
3
4class Solution:
5 def checkValid(self, matrix: List[List[int]]) -> bool:
6 """
7 Check if a matrix is valid (each row and column contains all numbers from 1 to n exactly once).
8
9 Args:
10 matrix: n x n matrix to validate
11
12 Returns:
13 True if matrix is valid, False otherwise
14 """
15 n = len(matrix)
16
17 # Check all rows and columns:
18 # - matrix gives us all rows
19 # - zip(*matrix) transposes the matrix to give us all columns
20 # - chain combines both iterables into a single sequence
21 # - For each row/column, convert to set and check if it has exactly n unique elements
22 return all(len(set(row)) == n for row in chain(matrix, zip(*matrix)))
23
1class Solution {
2 /**
3 * Checks if an n x n matrix is valid.
4 * A valid matrix has each row and column containing all integers from 1 to n exactly once.
5 *
6 * @param matrix The n x n matrix to validate
7 * @return true if the matrix is valid, false otherwise
8 */
9 public boolean checkValid(int[][] matrix) {
10 int n = matrix.length;
11
12 // Boolean array to track visited numbers (1 to n)
13 // Index 0 is unused since valid numbers are from 1 to n
14 boolean[] visited = new boolean[n + 1];
15
16 // Check each row for duplicates and valid range
17 for (int[] row : matrix) {
18 // Reset visited array for each row
19 Arrays.fill(visited, false);
20
21 for (int value : row) {
22 // If number already seen in this row, matrix is invalid
23 if (visited[value]) {
24 return false;
25 }
26 // Mark this number as seen
27 visited[value] = true;
28 }
29 }
30
31 // Check each column for duplicates and valid range
32 for (int col = 0; col < n; ++col) {
33 // Reset visited array for each column
34 Arrays.fill(visited, false);
35
36 for (int row = 0; row < n; ++row) {
37 int value = matrix[row][col];
38
39 // If number already seen in this column, matrix is invalid
40 if (visited[value]) {
41 return false;
42 }
43 // Mark this number as seen
44 visited[value] = true;
45 }
46 }
47
48 // All rows and columns contain unique values from 1 to n
49 return true;
50 }
51}
52
1class Solution {
2public:
3 bool checkValid(vector<vector<int>>& matrix) {
4 int n = matrix.size();
5
6 // Check each row contains all numbers from 1 to n
7 for (const auto& row : matrix) {
8 // Create a visited array to track seen numbers (1-indexed)
9 vector<bool> visited(n + 1, false);
10
11 for (int num : row) {
12 // If we've seen this number before in the current row, it's invalid
13 if (visited[num]) {
14 return false;
15 }
16 visited[num] = true;
17 }
18 }
19
20 // Check each column contains all numbers from 1 to n
21 for (int col = 0; col < n; ++col) {
22 // Create a visited array to track seen numbers (1-indexed)
23 vector<bool> visited(n + 1, false);
24
25 for (int row = 0; row < n; ++row) {
26 int num = matrix[row][col];
27 // If we've seen this number before in the current column, it's invalid
28 if (visited[num]) {
29 return false;
30 }
31 visited[num] = true;
32 }
33 }
34
35 // All rows and columns contain unique numbers from 1 to n
36 return true;
37 }
38};
39
1/**
2 * Checks if an n×n matrix is valid (contains all numbers from 1 to n in each row and column)
3 * @param matrix - The n×n matrix to validate
4 * @returns true if the matrix is valid, false otherwise
5 */
6function checkValid(matrix: number[][]): boolean {
7 const n: number = matrix.length;
8 // Array to track which numbers have been seen (indices 1 to n)
9 const visited: boolean[] = Array(n + 1).fill(false);
10
11 // Check each row contains all numbers from 1 to n exactly once
12 for (const row of matrix) {
13 // Reset visited array for each row
14 visited.fill(false);
15
16 for (const value of row) {
17 // If we've already seen this number in the current row, it's invalid
18 if (visited[value]) {
19 return false;
20 }
21 // Mark this number as seen
22 visited[value] = true;
23 }
24 }
25
26 // Check each column contains all numbers from 1 to n exactly once
27 for (let col: number = 0; col < n; ++col) {
28 // Reset visited array for each column
29 visited.fill(false);
30
31 for (let row: number = 0; row < n; ++row) {
32 const value: number = matrix[row][col];
33 // If we've already seen this number in the current column, it's invalid
34 if (visited[value]) {
35 return false;
36 }
37 // Mark this number as seen
38 visited[value] = true;
39 }
40 }
41
42 // All rows and columns are valid
43 return true;
44}
45
Time and Space Complexity
Time Complexity: O(n^2)
The code iterates through all elements in the matrix twice:
- First iteration: checking each row in
matrix
(n rows × n elements per row =n^2
operations) - Second iteration: checking each column via
zip(*matrix)
which transposes the matrix (n columns × n elements per column =n^2
operations)
For each row/column, set(row)
creates a set which takes O(n)
time, and this is done 2n
times total. Additionally, len(set(row))
comparison is O(1)
. Therefore, the total time complexity is O(n) × 2n = O(n^2)
.
Space Complexity: O(n)
The space complexity comes from:
- Creating a set for each row/column check, which can contain at most
n
elements:O(n)
- The
zip(*matrix)
operation creates a transposed view of the matrix, but since it's evaluated lazily in the generator expression and only one column is processed at a time, it usesO(n)
space for the current column being checked - The
chain
iterator itself usesO(1)
space
The maximum space used at any point is O(n)
for the set creation during validation of each row or column.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Not Validating the Actual Values
The current solution only checks if each row and column has n
unique elements, but doesn't verify that these elements are specifically the integers from 1 to n.
For example, if n = 3
and a row contains [0, 2, 4]
, the solution would incorrectly return True
because there are 3 unique elements, even though the valid values should be [1, 2, 3]
.
Example of incorrect validation:
matrix = [[0, 2], [3, 4]] # n=2, but contains 0,3,4 instead of 1,2 # Current solution returns True (incorrectly) # Should return False
Solution: Add explicit value range checking:
def checkValid(self, matrix: List[List[int]]) -> bool:
n = len(matrix)
valid_set = set(range(1, n + 1)) # {1, 2, ..., n}
# Check if each row contains exactly the values 1 to n
for row in matrix:
if set(row) != valid_set:
return False
# Check if each column contains exactly the values 1 to n
for col in zip(*matrix):
if set(col) != valid_set:
return False
return True
Pitfall 2: Assuming Matrix is Square
The solution assumes the input matrix is always square (n x n
), but doesn't handle edge cases where the matrix might be rectangular or empty.
Example of edge case:
matrix = [[1, 2, 3], [3, 1, 2]] # 2x3 matrix, not square # Could cause unexpected behavior
Solution: Add validation for matrix dimensions:
def checkValid(self, matrix: List[List[int]]) -> bool:
if not matrix or not matrix[0]:
return False
n = len(matrix)
# Verify matrix is square
if any(len(row) != n for row in matrix):
return False
valid_set = set(range(1, n + 1))
return all(set(row) == valid_set for row in chain(matrix, zip(*matrix)))
Pitfall 3: Performance with Large Matrices
While the current solution is already O(n²), creating sets repeatedly for each row and column can be memory-intensive for very large matrices, especially when using chain()
which might create additional overhead.
Solution for better memory usage: Process rows and columns separately to avoid holding too many temporary objects:
def checkValid(self, matrix: List[List[int]]) -> bool:
n = len(matrix)
valid_set = set(range(1, n + 1))
# Check rows first
if not all(set(row) == valid_set for row in matrix):
return False
# Then check columns
return all(set(col) == valid_set for col in zip(*matrix))
This approach allows earlier termination if rows are invalid, potentially saving computation on columns.
A heap is a ...?
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!