title: Brick Wall
slug: brick-wall
difficulty: medium
leetcode_id: 554
leetcode_url: https://leetcode.com/problems/brick-wall/
categories:
  - arrays
  - hash-tables
patterns:
  - prefix-sum

description: |
  There is a rectangular brick wall in front of you with `n` rows of bricks. The i<sup>th</sup> row has some number of bricks each of the same height (i.e., one unit) but they can be of different widths. The total width of each row is the same.

  Draw a vertical line from the top to the bottom and cross the **least bricks**. If your line goes through the edge of a brick, then the brick is not considered as crossed. You cannot draw a line just along one of the two vertical edges of the wall, in which case the line will obviously cross no bricks.

  Given the 2D array `wall` that contains the information about the wall, return *the minimum number of crossed bricks after drawing such a vertical line*.

constraints: |
  - `n == wall.length`
  - `1 <= n <= 10^4`
  - `1 <= wall[i].length <= 10^4`
  - `1 <= sum(wall[i].length) <= 2 * 10^4`
  - `sum(wall[i])` is the same for each row `i`
  - `1 <= wall[i][j] <= 2^31 - 1`

examples:
  - input: "wall = [[1,2,2,1],[3,1,2],[1,3,2],[2,4],[3,1,2],[1,3,1,1]]"
    output: "2"
    explanation: "Drawing a vertical line at position 4 (from the left) passes through the edges of bricks in 4 rows, crossing only 2 bricks."
  - input: "wall = [[1],[1],[1]]"
    output: "3"
    explanation: "Each row has only one brick spanning the entire width. Any vertical line must cross all 3 bricks."

explanation:
  intuition: |
    Imagine you're looking at the wall from above, marking where all the brick edges line up. Some vertical positions have many edges aligned (like a seam running through multiple rows), while others have few.

    The **core insight** is to flip the problem: instead of minimising bricks crossed, **maximise edges passed through**. If a vertical line passes through `k` edges, it crosses `n - k` bricks (where `n` is the total number of rows).

    Think of it like this: each brick edge creates a "gap" that a vertical line can slip through without crossing that brick. By counting how many gaps align at each horizontal position, we find where to draw the line.

    We use a hash map to count edge positions. For each row, we compute the cumulative width (prefix sum) as we go through its bricks. Each cumulative sum (except the rightmost edge) represents an edge position. The position with the highest count is our optimal line placement.

  approach: |
    We solve this using a **Hash Map Edge Counting** approach:

    **Step 1: Initialise an edge counter**

    - `edge_count`: A hash map (dictionary) that maps each horizontal position to the number of brick edges at that position

    &nbsp;

    **Step 2: Iterate through each row**

    - For each row, track the cumulative width as we iterate through its bricks
    - For each brick (except the last one in the row), add its width to the cumulative sum
    - This cumulative sum represents an edge position — increment its count in the hash map
    - We skip the last brick because its edge is the wall boundary (not a valid line position)

    &nbsp;

    **Step 3: Find the maximum edge count**

    - Scan the hash map to find the position with the most edges aligned
    - If the hash map is empty (every row has only one brick), the maximum is `0`

    &nbsp;

    **Step 4: Calculate the answer**

    - Return `n - max_edges`, where `n` is the number of rows
    - This gives us the minimum number of bricks crossed

    &nbsp;

    The key observation is that we transform a minimisation problem into a maximisation problem, making it much easier to solve with simple counting.

  common_pitfalls:
    - title: Including the Wall Boundary
      description: |
        A common mistake is to include the rightmost edge of each row (the wall boundary) in the edge count.

        For `wall = [[3], [3], [3]]`, if you count position `3` for each row, you'd incorrectly find `max_edges = 3` and return `0` bricks crossed.

        But the problem states: "You cannot draw a line just along one of the two vertical edges of the wall." Always exclude the last brick's edge from your counting.
      wrong_approach: "Counting cumulative sum of ALL bricks including the last"
      correct_approach: "Skip the last brick in each row when counting edges"

    - title: Brute Force Position Checking
      description: |
        A naive approach is to iterate through every possible x-position and count how many bricks it crosses.

        With wall width up to `2^31 - 1`, this is impossibly slow. The insight is that we only need to consider positions where edges exist — the hash map naturally handles this by only storing edge positions.
      wrong_approach: "Checking every integer x from 1 to wall_width-1"
      correct_approach: "Only consider positions where at least one edge exists"

    - title: Empty Hash Map Edge Case
      description: |
        When every row contains exactly one brick (e.g., `[[1], [1], [1]]`), no internal edges exist. The hash map will be empty.

        In this case, `max_edges = 0`, so the answer is `n - 0 = n`. Make sure your code handles the case where the hash map has no entries.

  key_takeaways:
    - "**Flip the problem**: Minimising bricks crossed is equivalent to maximising edges passed through"
    - "**Prefix sum for positions**: Cumulative sums of brick widths give you the edge positions"
    - "**Hash map counting**: When you need to find the most common value in a stream, a hash map is the natural choice"
    - "**Boundary awareness**: Be careful about edge cases involving boundaries — they're often excluded from valid solutions"

  time_complexity: "O(n * m). We visit each brick once across all rows, where `n` is the number of rows and `m` is the average number of bricks per row."
  space_complexity: "O(w). The hash map stores at most `w` unique edge positions, where `w` is bounded by the total number of bricks."

solutions:
  - approach_name: Hash Map Edge Counting
    is_optimal: true
    code: |
      def least_bricks(wall: list[list[int]]) -> int:
          # Hash map to count edges at each horizontal position
          edge_count = {}

          for row in wall:
              # Track cumulative width as we traverse the row
              position = 0

              # Skip the last brick (its edge is the wall boundary)
              for i in range(len(row) - 1):
                  position += row[i]
                  # Increment edge count at this position
                  edge_count[position] = edge_count.get(position, 0) + 1

          # Find the position with the most edges aligned
          # If no edges exist (single-brick rows), max_edges is 0
          max_edges = max(edge_count.values()) if edge_count else 0

          # Minimum bricks crossed = total rows - edges we pass through
          return len(wall) - max_edges
    explanation: |
      **Time Complexity:** O(n * m) — We iterate through each brick in the wall once.

      **Space Complexity:** O(w) — The hash map stores edge positions, bounded by total brick count.

      By counting edges instead of counting crossed bricks, we transform the problem into finding the maximum value in a frequency map. The answer is simply the total number of rows minus the maximum edge count.

  - approach_name: Hash Map with Counter
    is_optimal: true
    code: |
      from collections import Counter

      def least_bricks(wall: list[list[int]]) -> int:
          # Use Counter for cleaner frequency counting
          edge_count = Counter()

          for row in wall:
              position = 0
              # Accumulate edge positions (excluding last brick)
              for brick_width in row[:-1]:
                  position += brick_width
                  edge_count[position] += 1

          # max() on empty Counter raises error, so handle it
          max_edges = max(edge_count.values(), default=0)

          return len(wall) - max_edges
    explanation: |
      **Time Complexity:** O(n * m) — Same as the basic approach.

      **Space Complexity:** O(w) — Same space usage for the Counter.

      This is a Pythonic variant using `Counter` from the collections module. The `default=0` in `max()` handles the empty case elegantly. Using `row[:-1]` is a clean way to skip the last brick.
