title: Check if Every Row and Column Contains All Numbers
slug: check-if-every-row-and-column-contains-all-numbers
difficulty: easy
leetcode_id: 2133
leetcode_url: https://leetcode.com/problems/check-if-every-row-and-column-contains-all-numbers/
categories:
  - arrays
  - hash-tables
patterns:
  - matrix-traversal

function_signature: "def check_valid(matrix: list[list[int]]) -> bool:"

test_cases:
  visible:
    - input: { matrix: [[1, 2, 3], [3, 1, 2], [2, 3, 1]] }
      expected: true
    - input: { matrix: [[1, 1, 1], [1, 2, 3], [1, 2, 3]] }
      expected: false
  hidden:
    - input: { matrix: [[1]] }
      expected: true
    - input: { matrix: [[1, 2], [2, 1]] }
      expected: true
    - input: { matrix: [[1, 2], [1, 2]] }
      expected: false
    - input: { matrix: [[2, 1], [1, 2]] }
      expected: true
    - input: { matrix: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]] }
      expected: true
    - input: { matrix: [[1, 2, 3], [2, 3, 1], [3, 1, 3]] }
      expected: false

description: |
  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`.

constraints: |
  - `n == matrix.length == matrix[i].length`
  - `1 <= n <= 100`
  - `1 <= matrix[i][j] <= n`

examples:
  - 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."
  - 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."

explanation:
  intuition: |
    Think of this problem like checking a **simplified Sudoku**. In a valid Sudoku, each row and column must contain every number from 1 to 9 exactly once. Here, we have the same requirement for numbers 1 to n.

    The core insight is that if a row (or column) of size `n` must contain all numbers from 1 to n, then each number must appear **exactly once**. This means:
    - No duplicates are allowed
    - No numbers outside the range `[1, n]` are allowed (though the constraints guarantee this)

    A set is the perfect data structure for this check. When you add `n` elements to a set, the set's size will be `n` only if all elements were unique. If there were any duplicates, the size would be smaller.

    By checking each row and each column against this property, we can determine if the matrix is valid.

  approach: |
    We solve this using a **Set-Based Validation** approach:

    **Step 1: Determine the matrix size**

    - Get `n` from the length of the matrix
    - This tells us each row and column must contain exactly the numbers `1` through `n`

    &nbsp;

    **Step 2: Validate each row**

    - For each row in the matrix, convert it to a set
    - Check if the set equals `{1, 2, 3, ..., n}`
    - If any row fails this check, return `false` immediately

    &nbsp;

    **Step 3: Validate each column**

    - For each column index `j`, collect all elements `matrix[i][j]` for `i` from `0` to `n-1`
    - Convert this column to a set
    - Check if the set equals `{1, 2, 3, ..., n}`
    - If any column fails this check, return `false` immediately

    &nbsp;

    **Step 4: Return the result**

    - If all rows and columns pass validation, return `true`

  common_pitfalls:
    - title: Checking Only Rows or Only Columns
      description: |
        A matrix might have valid rows but invalid columns (or vice versa).

        For example, `[[1,2],[1,2]]` has valid rows (both contain `{1, 2}`), but the first column is `{1, 1}` and the second is `{2, 2}` — neither column is valid.

        Always check **both** rows and columns before returning `true`.
      wrong_approach: "Only validating rows"
      correct_approach: "Validate both rows and columns"

    - title: Counting Instead of Using Sets
      description: |
        You might try counting occurrences and checking if each number appears exactly once. While this works, it's more complex and error-prone.

        Using a set is cleaner: if the set of elements equals `{1, 2, ..., n}`, the row/column is valid. This automatically handles duplicate detection and range validation in one check.
      wrong_approach: "Manual counting with arrays"
      correct_approach: "Set comparison for cleaner validation"

    - title: Not Failing Fast
      description: |
        Some solutions check all rows and columns before deciding validity. This is wasteful — as soon as you find one invalid row or column, you know the entire matrix is invalid.

        Return `false` immediately when validation fails to optimise performance.
      wrong_approach: "Check everything, then decide"
      correct_approach: "Return false at first failure"

  key_takeaways:
    - "**Sets for uniqueness**: When checking for duplicates or that all elements in a range are present, sets provide an elegant O(n) solution"
    - "**Matrix traversal pattern**: Accessing columns requires iterating with a fixed column index: `matrix[i][col]` for all rows `i`"
    - "**Fail fast principle**: Return early when you find invalid data rather than completing all checks unnecessarily"
    - "**Simplified Sudoku**: This problem is a building block for understanding more complex grid validation like Sudoku"

  time_complexity: "O(n^2). We visit each cell exactly twice — once when checking its row and once when checking its column."
  space_complexity: "O(n). We use a set that holds at most `n` elements at any time for validation."

solutions:
  - approach_name: Set-Based Validation
    is_optimal: true
    code: |
      def check_valid(matrix: list[list[int]]) -> bool:
          n = len(matrix)
          # The set of numbers that must appear in each row/column
          expected = set(range(1, n + 1))

          # Check each row
          for row in matrix:
              if set(row) != expected:
                  return False

          # Check each column
          for col in range(n):
              # Collect all elements in this column
              column_set = {matrix[row][col] for row in range(n)}
              if column_set != expected:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n^2) — We iterate through all n^2 elements twice (once for rows, once for columns).

      **Space Complexity:** O(n) — The sets we create hold at most n elements.

      We create the expected set `{1, 2, ..., n}` once, then compare each row and column against it. Using set comprehension for columns keeps the code clean and Pythonic.

  - approach_name: Early Termination with Size Check
    is_optimal: true
    code: |
      def check_valid(matrix: list[list[int]]) -> bool:
          n = len(matrix)

          # Check rows - set size must equal n (all unique 1 to n)
          for row in matrix:
              if len(set(row)) != n:
                  return False

          # Check columns
          for col in range(n):
              column_values = set()
              for row in range(n):
                  column_values.add(matrix[row][col])
              if len(column_values) != n:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n^2) — Same as the previous approach.

      **Space Complexity:** O(n) — Set holds at most n elements.

      This variant uses size checking instead of set equality. Since the constraints guarantee `1 <= matrix[i][j] <= n`, if we have `n` unique values, they must be exactly `{1, 2, ..., n}`. This avoids creating the expected set but relies on the constraint being enforced.
