title: Check If All 1's Are at Least Length K Places Away
slug: check-if-all-1s-are-at-least-length-k-places-away
difficulty: easy
leetcode_id: 1437
leetcode_url: https://leetcode.com/problems/check-if-all-1s-are-at-least-length-k-places-away/
categories:
  - arrays
patterns:
  - two-pointers

description: |
  Given a binary array `nums` and an integer `k`, return `true` *if all* `1`*'s are at least* `k` *places away from each other, otherwise return* `false`.

  In other words, for every pair of `1`s in the array, there must be at least `k` elements (zeros) between them.

constraints: |
  - `1 <= nums.length <= 10^5`
  - `0 <= k <= nums.length`
  - `nums[i]` is `0` or `1`

examples:
  - input: "nums = [1,0,0,0,1,0,0,1], k = 2"
    output: "true"
    explanation: "Each of the 1s are at least 2 places away from each other. The first and second 1s have 3 zeros between them, and the second and third 1s have 2 zeros between them."
  - input: "nums = [1,0,0,1,0,1], k = 2"
    output: "false"
    explanation: "The second 1 (index 3) and third 1 (index 5) are only one position apart from each other (distance = 2 - 1 = 1 < k)."

explanation:
  intuition: |
    Think of this problem as checking distances between consecutive markers on a number line.

    Imagine the array as a row of positions, where some positions have a flag (`1`) and others are empty (`0`). Your task is to verify that every pair of *adjacent* flags has at least `k` empty positions between them.

    The key insight is that you only need to check **consecutive** pairs of `1`s. If every consecutive pair has sufficient distance, then all pairs automatically satisfy the condition. This is because if `1` at position A and `1` at position B are far enough apart, and `1` at position B and `1` at position C are far enough apart, then A and C are definitely far enough apart (distance(A,C) = distance(A,B) + distance(B,C)).

    As you iterate through the array, whenever you encounter a `1`, you check how far it is from the *previous* `1`. If this distance is less than or equal to `k`, the condition is violated.

  approach: |
    We solve this using a **Single Pass with Position Tracking** approach:

    **Step 1: Initialise tracking variable**

    - `prev_one_index`: Set to `-k - 1` initially (or a very negative value), so that the first `1` we encounter won't falsely trigger a violation

    &nbsp;

    **Step 2: Iterate through the array**

    - For each index `i`, check if `nums[i] == 1`
    - If it's a `1`, calculate the distance from the previous `1`: `i - prev_one_index`
    - If this distance is less than or equal to `k`, return `false` immediately
    - Otherwise, update `prev_one_index = i` to track this `1` as the new reference point

    &nbsp;

    **Step 3: Return the result**

    - If we complete the loop without returning `false`, all `1`s are sufficiently spaced
    - Return `true`

    &nbsp;

    The trick of initialising `prev_one_index` to `-k - 1` ensures the first `1` automatically passes the distance check (since `i - (-k-1) = i + k + 1 > k` for any non-negative `i`).

  common_pitfalls:
    - title: Off-by-One in Distance Calculation
      description: |
        A common mistake is confusing "k places away" with "distance of k".

        If two `1`s are at indices 2 and 5, the distance is `5 - 2 = 3`, meaning there are **2 zeros** between them (indices 3 and 4).

        Being "k places away" means the **distance** (difference in indices) must be **greater than k**, not greater than or equal to. With `k = 2`, a distance of exactly 2 means only 1 zero between them, which violates the condition.

        The check should be `distance <= k` returns `false`, or equivalently, we need `distance > k` to pass.
      wrong_approach: "Checking if distance >= k"
      correct_approach: "Checking if distance > k (or distance <= k returns false)"

    - title: Forgetting to Handle the First 1
      description: |
        If you initialise `prev_one_index` to `0` or don't handle it specially, you might incorrectly flag the first `1` as violating the condition.

        For example, with `nums = [0,0,1,...]`, if `prev_one_index = 0`, then when we reach index 2, we'd compute distance = 2, which could falsely fail for `k >= 2`.

        Initialising to `-k - 1` (or any value ≤ `-k - 1`) ensures the first `1` always passes since its "distance from the previous" will always be greater than `k`.
      wrong_approach: "Initialising prev_one_index to 0"
      correct_approach: "Initialising prev_one_index to -k - 1 or using a flag for first occurrence"

    - title: Checking All Pairs Instead of Consecutive
      description: |
        A brute force approach might store all indices of `1`s and check every pair combination, resulting in O(n^2) worst case if many `1`s exist.

        This is unnecessary because checking only consecutive pairs is sufficient. If A-B and B-C satisfy the distance requirement, A-C automatically does too.
      wrong_approach: "O(n^2) checking all pairs of 1s"
      correct_approach: "O(n) checking only consecutive pairs"

  key_takeaways:
    - "**Single-pass pattern**: Many array problems can be solved by tracking key information (like the last occurrence) as you iterate"
    - "**Initialisation trick**: Setting the initial state to a \"safe\" value (like `-k - 1`) eliminates the need for special-case handling"
    - "**Consecutive suffices**: When checking pairwise properties with transitivity, you only need to verify consecutive pairs"
    - "**Early termination**: Return `false` as soon as a violation is found — no need to continue checking"

  time_complexity: "O(n). We traverse the array exactly once, performing constant-time operations at each element."
  space_complexity: "O(1). We only use a single integer variable (`prev_one_index`) regardless of the input size."

solutions:
  - approach_name: Single Pass with Position Tracking
    is_optimal: true
    code: |
      def k_length_apart(nums: list[int], k: int) -> bool:
          # Initialise to a value that ensures the first 1 passes
          # Any index minus this will be > k
          prev_one_index = -k - 1

          for i, num in enumerate(nums):
              if num == 1:
                  # Check distance from previous 1
                  if i - prev_one_index <= k:
                      # Distance is too small, condition violated
                      return False
                  # Update the position of the last seen 1
                  prev_one_index = i

          # All consecutive 1s are sufficiently spaced
          return True
    explanation: |
      **Time Complexity:** O(n) — Single pass through the array.

      **Space Complexity:** O(1) — Only one variable used.

      We iterate through the array once, tracking the index of the most recent `1`. When we encounter a new `1`, we check if the distance from the previous `1` exceeds `k`. The clever initialisation of `prev_one_index = -k - 1` handles the edge case of the first `1` elegantly.

  - approach_name: Collect Indices Then Check
    is_optimal: false
    code: |
      def k_length_apart(nums: list[int], k: int) -> bool:
          # Collect all indices where nums[i] == 1
          one_indices = [i for i, num in enumerate(nums) if num == 1]

          # Check consecutive pairs
          for i in range(1, len(one_indices)):
              if one_indices[i] - one_indices[i - 1] <= k:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n) — Two passes: one to collect indices, one to check pairs.

      **Space Complexity:** O(m) — Where m is the number of 1s in the array (could be O(n) in worst case).

      This approach first collects all positions of `1`s into a list, then checks consecutive pairs. While still O(n) time, it uses extra space and requires two passes. The single-pass approach is preferred for both space efficiency and early termination capability.
