Check if Every Row and Column Contains All Numbers
EasyPrompt
An n x n matrix is valid if every row and every column contains all the integers from 1 to n (inclusive).
Given an n x n integer matrix matrix, return true if the matrix is valid. Otherwise, return false.
Example 1:
Input: matrix = [[1,2,3],[3,1,2],[2,3,1]]
Output: true
Explanation: In this case, n = 3, and every row and column contains the numbers 1, 2, and 3.
Hence, we return true.Example 2:
Input: matrix = [[1,1,1],[1,2,3],[1,2,3]]
Output: false
Explanation: In this case, n = 3, but the first row and the first column do not contain the numbers 2 or 3.
Hence, we return false.
Constraints:
n == matrix.length == matrix[i].length1 <= n <= 1001 <= matrix[i][j] <= n
Approaches
3 approaches with complexity analysis and trade-offs.
This approach uses sorting to validate each row and column. The core idea is that if a row or column contains all numbers from 1 to n exactly once, then after sorting, it must be identical to the sequence 1, 2, 3, ..., n. We can check this property for every row and every column.
Algorithm
- Iterate through each row of the matrix from
i = 0ton-1. - For each row, create a temporary array and copy the row's elements into it.
- Sort the temporary array.
- After sorting, iterate through the temporary array. The element at index
jshould bej+1. If this condition is not met for any element, it means the row is invalid. Returnfalse. - Repeat the same process for each column. Iterate from
j = 0ton-1. - For each column, create a temporary array and populate it with the column's elements.
- Sort the column array and check if its elements are
1, 2, ..., nin order. If not, returnfalse. - If all rows and columns pass the checks, the matrix is valid. Return
true.
Walkthrough
We can break down the problem into two main parts: validating all rows and validating all columns.
For row validation, we iterate through each row. For a given row, we create a new array containing its elements. We then sort this new array. A valid row, when sorted, will have its elements in ascending order from 1 to n. So, we check if the element at index j of the sorted array is equal to j + 1. If we find any mismatch, we can immediately conclude the matrix is invalid and return false.
Similarly, for column validation, we iterate through each column. For each column, we create a temporary array and fill it with the elements from that column. We then sort this temporary array and perform the same check as we did for the rows. If any column is invalid, we return false.
If we successfully check all n rows and n columns without finding any issues, it means the matrix is valid, and we can return true.
import java.util.Arrays; class Solution { public boolean checkValid(int[][] matrix) { int n = matrix.length; // Check each row for (int i = 0; i < n; i++) { int[] tempRow = new int[n]; for (int j = 0; j < n; j++) { tempRow[j] = matrix[i][j]; } Arrays.sort(tempRow); for (int j = 0; j < n; j++) { if (tempRow[j] != j + 1) { return false; } } } // Check each column for (int j = 0; j < n; j++) { int[] tempCol = new int[n]; for (int i = 0; i < n; i++) { tempCol[i] = matrix[i][j]; } Arrays.sort(tempCol); for (int i = 0; i < n; i++) { if (tempCol[i] != i + 1) { return false; } } } return true; }}Complexity
Time
O(n^2 log n) - We iterate through `n` rows and `n` columns. For each, we create a copy (O(n)) and sort it (O(n log n)). The total time is `n * O(n log n)` for rows and `n * O(n log n)` for columns, leading to an overall complexity of `O(n^2 log n)`.
Space
O(n) - We need an auxiliary array of size `n` to hold the elements of a row or a column for sorting.
Trade-offs
Pros
The logic is straightforward and easy to understand.
Cons
The time complexity of
O(n^2 log n)is suboptimal for this problem.Creating and sorting temporary arrays for each row and column is computationally expensive.
A more efficient approach is to use a hash set to check for the uniqueness of elements in each row and column. The problem states that all numbers are between 1 and n. Therefore, if a row or column of size n contains n unique numbers, it must contain all numbers from 1 to n.
We can iterate through each row, add its elements to a hash set, and then check if the set's size is n. We do the same for each column. If any check fails, the matrix is invalid.
Algorithm
- Iterate through each row of the matrix from
i = 0ton-1. - For each row, create an empty
HashSet. - Iterate through the elements of the current row and add each element to the set.
- After processing the row, check if the size of the set is equal to
n. If not, it implies there were duplicate numbers, so the row is invalid. Returnfalse. - Repeat the same process for each column. Iterate from
j = 0ton-1. - For each column, use a
HashSetto collect its elements. - If the set's size is not
nfor any column, returnfalse. - If all rows and columns are validated, return
true.
Walkthrough
This method leverages the properties of a HashSet, which only stores unique elements. The validation process is again split into checking rows and columns separately.
To check the rows, we loop from i = 0 to n-1. In each iteration, we initialize a new HashSet. We then traverse the i-th row, adding each element matrix[i][j] to the set. Since a set automatically handles duplicates (i.e., adding an existing element does nothing), we can determine if the row is valid by its final state. After iterating through all elements in the row, if the size() of the set is less than n, it means there was at least one duplicate number, and the row is not a valid permutation of 1...n. In this case, we return false.
We apply the exact same logic to the columns. We loop from j = 0 to n-1, create a new HashSet for each column, populate it with the column's elements, and check if its final size is n. If any column fails this test, we return false.
If all 2n checks (for n rows and n columns) pass, the matrix is valid, and we return true.
import java.util.HashSet;import java.util.Set; class Solution { public boolean checkValid(int[][] matrix) { int n = matrix.length; // Check each row for (int i = 0; i < n; i++) { Set<Integer> rowSet = new HashSet<>(); for (int j = 0; j < n; j++) { rowSet.add(matrix[i][j]); } if (rowSet.size() != n) { return false; } } // Check each column for (int j = 0; j < n; j++) { Set<Integer> colSet = new HashSet<>(); for (int i = 0; i < n; i++) { colSet.add(matrix[i][j]); } if (colSet.size() != n) { return false; } } return true; }}Complexity
Time
O(n^2) - We traverse the `n x n` matrix twice. Once for rows (`n` rows * `n` elements = `O(n^2)`) and once for columns (`n` columns * `n` elements = `O(n^2)`). The total time is `O(n^2) + O(n^2) = O(n^2)`.
Space
O(n) - For each row or column check, a `HashSet` is created which can store up to `n` elements.
Trade-offs
Pros
Time complexity of
O(n^2)is a significant improvement over the sorting approach.The logic is still relatively easy to follow.
Cons
Requires two separate passes over the matrix data (one for rows, one for columns).
Using a
HashSethas more overhead (both in memory and time) compared to a simple boolean array, although the asymptotic complexity is the same.
This is the most optimized approach. We can improve on the previous method by combining the row and column checks into a single pass and by using a boolean array instead of a HashSet. Since the numbers are constrained to be within [1, n], a boolean array of size n+1 can serve as a direct-access hash set, which is faster and more memory-efficient.
In a single main loop, we can validate the i-th row and the i-th column at the same time. This reduces code duplication and can have minor performance benefits due to better data locality.
Algorithm
- Get the size of the matrix,
n. - Start a single loop that iterates from
i = 0ton-1. This loop will handle the validation of thei-th row and thei-th column simultaneously. - Inside this loop, create two boolean arrays,
rowCheckandcolCheck, each of sizen+1. These will act as frequency maps. - Start a nested loop that iterates from
j = 0ton-1. - In the inner loop, check the
i-th row: letrowVal = matrix[i][j]. IfrowCheck[rowVal]is alreadytrue, we have found a duplicate in the row. Returnfalse. - Mark the number as seen for the current row:
rowCheck[rowVal] = true. - Simultaneously, check the
i-th column: letcolVal = matrix[j][i]. IfcolCheck[colVal]is alreadytrue, we have found a duplicate in the column. Returnfalse. - Mark the number as seen for the current column:
colCheck[colVal] = true. - If the outer loop completes without returning
false, it means every row and every column has been validated successfully. Returntrue.
Walkthrough
The key optimization here is to perform all checks within a single O(n^2) traversal of the matrix's indices. We can iterate from i = 0 to n-1, and in each iteration, we focus on validating both row i and column i.
For each i, we declare two boolean arrays, rowCheck and colCheck, of size n+1, initialized to false. These arrays will track which numbers we've seen in the current row and column, respectively. The index of the array corresponds to the number (e.g., rowCheck[5] corresponds to the number 5).
We then use a nested loop with index j from 0 to n-1. Inside this loop, we perform two checks:
- Row Check: We look at the element
matrix[i][j]. IfrowCheck[matrix[i][j]]is alreadytrue, it means we've seen this number before in the current row, indicating a duplicate. We immediately returnfalse. - Column Check: We look at the element
matrix[j][i]. IfcolCheck[matrix[j][i]]istrue, we've found a duplicate in the current column and returnfalse.
If the numbers are not duplicates, we mark them as seen by setting rowCheck[matrix[i][j]] = true and colCheck[matrix[j][i]] = true.
By the time the inner j loop finishes, we have fully validated row i and column i. The outer i loop ensures that every row and every column is eventually checked. If the entire process completes without returning false, the matrix is valid.
class Solution { public boolean checkValid(int[][] matrix) { int n = matrix.length; for (int i = 0; i < n; i++) { boolean[] rowCheck = new boolean[n + 1]; boolean[] colCheck = new boolean[n + 1]; for (int j = 0; j < n; j++) { // Check for duplicates in row i int rowVal = matrix[i][j]; if (rowCheck[rowVal]) { return false; } rowCheck[rowVal] = true; // Check for duplicates in column i int colVal = matrix[j][i]; if (colCheck[colVal]) { return false; } colCheck[colVal] = true; } } return true; }}Complexity
Time
O(n^2) - We have a nested loop structure where the outer loop runs `n` times and the inner loop runs `n` times. All operations inside the loops are constant time. Thus, the total time complexity is `O(n*n) = O(n^2)`.
Space
O(n) - In each iteration of the outer loop, we create two boolean arrays of size `n+1`. The space is reused in each iteration, so the peak space complexity is `O(n)`.
Trade-offs
Pros
Most efficient approach with
O(n^2)time complexity.Uses boolean arrays which are faster and more memory-efficient than hash sets.
Combines row and column checks into a single, elegant loop structure.
Cons
The logic of checking row
iand columniin the same outer loop iteration can be slightly less intuitive than checking all rows then all columns.
Solutions
Solution
class Solution {public boolean checkValid(int[][] matrix) { int n = matrix.length; for (int i = 0; i < n; ++i) { boolean[] seen = new boolean[n]; for (int j = 0; j < n; ++j) { int v = matrix[i][j] - 1; if (seen[v]) { return false; } seen[v] = true; } } for (int j = 0; j < n; ++j) { boolean[] seen = new boolean[n]; for (int i = 0; i < n; ++i) { int v = matrix[i][j] - 1; if (seen[v]) { return false; } seen[v] = true; } } return true; }}class Solution {public: bool checkValid(vector<vector<int>> &matrix) { int n = matrix.size(); for (int i = 0; i < n; ++i) { vector<bool> seen(n); for (int j = 0; j < n; ++j) { int v = matrix[i][j] - 1; if (seen[v]) return false; seen[v] = true; } } for (int j = 0; j < n; ++j) { vector<bool> seen(n); for (int i = 0; i < n; ++i) { int v = matrix[i][j] - 1; if (seen[v]) return false; seen[v] = true; } } return true; }};class Solution: def checkValid(self, matrix: List[List[int]]) -> bool: n = len(matrix) for i in range(n): seen = [False] * n for j in range(n): v = matrix[i][j] - 1 if seen[v]: return False seen[v] = True for j in range(n): seen = [False] * n for i in range(n): v = matrix[i][j] - 1 if seen[v]: return False seen[v] = True return TrueVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.