Count Covered Buildings

MEDIUM

Description

You are given a positive integer n, representing an n x n city. You are also given a 2D grid buildings, where buildings[i] = [x, y] denotes a unique building located at coordinates [x, y].

A building is covered if there is at least one building in all four directions: left, right, above, and below.

Return the number of covered buildings.

 

Example 1:

Input: n = 3, buildings = [[1,2],[2,2],[3,2],[2,1],[2,3]]

Output: 1

Explanation:

  • Only building [2,2] is covered as it has at least one building:
    • above ([1,2])
    • below ([3,2])
    • left ([2,1])
    • right ([2,3])
  • Thus, the count of covered buildings is 1.

Example 2:

Input: n = 3, buildings = [[1,1],[1,2],[2,1],[2,2]]

Output: 0

Explanation:

  • No building has at least one building in all four directions.

Example 3:

Input: n = 5, buildings = [[1,3],[3,2],[3,3],[3,5],[5,3]]

Output: 1

Explanation:

  • Only building [3,3] is covered as it has at least one building:
    • above ([1,3])
    • below ([5,3])
    • left ([3,2])
    • right ([3,5])
  • Thus, the count of covered buildings is 1.

 

Constraints:

  • 2 <= n <= 105
  • 1 <= buildings.length <= 105
  • buildings[i] = [x, y]
  • 1 <= x, y <= n
  • All coordinates of buildings are unique.

Approaches

Checkout 3 different approaches to solve Count Covered Buildings. Click on different approaches to view the approach and algorithm in detail.

Grouping by Row/Column and Sorting

This approach improves upon the brute-force method by pre-processing the building locations. We group buildings by their row and column and then sort these groups. This allows for efficient searching of neighbors using binary search instead of a linear scan.

Algorithm

  • Create two maps: rows to group buildings by row, and cols to group by column. rows will map a row index x to a list of column indices y, and cols will map a column index y to a list of row indices x.
  • Populate these maps by iterating through the buildings array once.
  • Sort the lists of coordinates within each map entry.
  • Initialize a counter coveredBuildings to 0.
  • Iterate through each building (x, y) again.
  • For the current building:
    • Look up the sorted list of columns for its row x in the rows map. Use binary search to find the position of y. If y is not the first and not the last element, it has both a left and a right neighbor.
    • Look up the sorted list of rows for its column y in the cols map. Use binary search to find the position of x. If x is not the first and not the last element, it has both an above and a below neighbor.
  • If the building has neighbors in all four directions, increment coveredBuildings.
  • Return coveredBuildings.

To avoid the O(B^2) complexity, we can pre-process the data. By grouping all buildings by their row and column into HashMaps, we can quickly access all buildings on the same horizontal or vertical line. After grouping, we sort the buildings within each group. Now, for any given building (x, y), we can use binary search on the sorted list for its row x to see if there are buildings with smaller and larger y coordinates. Similarly, we check its column y for buildings with smaller and larger x coordinates. This reduces the search for neighbors from a linear scan to a logarithmic one.

import java.util.*;

class Solution {
    public int countCoveredBuildings(int n, int[][] buildings) {
        Map<Integer, List<Integer>> rows = new HashMap<>();
        Map<Integer, List<Integer>> cols = new HashMap<>();

        for (int[] building : buildings) {
            int x = building[0];
            int y = building[1];
            rows.computeIfAbsent(x, k -> new ArrayList<>()).add(y);
            cols.computeIfAbsent(y, k -> new ArrayList<>()).add(x);
        }

        for (List<Integer> list : rows.values()) {
            Collections.sort(list);
        }
        for (List<Integer> list : cols.values()) {
            Collections.sort(list);
        }

        int coveredCount = 0;
        for (int[] building : buildings) {
            int x = building[0];
            int y = building[1];

            List<Integer> rowList = rows.get(x);
            List<Integer> colList = cols.get(y);

            int yIndex = Collections.binarySearch(rowList, y);
            boolean hasLeft = yIndex > 0;
            boolean hasRight = yIndex < rowList.size() - 1;

            int xIndex = Collections.binarySearch(colList, x);
            boolean hasAbove = xIndex > 0;
            boolean hasBelow = xIndex < colList.size() - 1;

            if (hasLeft && hasRight && hasAbove && hasBelow) {
                coveredCount++;
            }
        }
        return coveredCount;
    }
}

Complexity Analysis

Time Complexity: O(B log B) - Where B is the number of buildings. Populating the maps is O(B). The dominant operation is sorting the lists within the maps, which can take up to O(B log B) in total. The final check involves B binary searches, also contributing to the O(B log B) complexity.Space Complexity: O(B) - The maps store each building's coordinates once, so the space is proportional to the number of buildings.

Pros and Cons

Pros:
  • Significantly faster than the brute-force approach.
  • Efficient enough to pass the given constraints.
Cons:
  • Requires additional space for the maps.
  • The sorting step adds a logarithmic factor to the time complexity.

Code Solutions

Checking out 3 solutions in different languages for Count Covered Buildings. Click on different languages to view the code.

class Solution {
public
  int countCoveredBuildings(int n, int[][] buildings) {
    Map<Integer, List<Integer>> g1 = new HashMap<>();
    Map<Integer, List<Integer>> g2 = new HashMap<>();
    for (int[] building : buildings) {
      int x = building[0], y = building[1];
      g1.computeIfAbsent(x, k->new ArrayList<>()).add(y);
      g2.computeIfAbsent(y, k->new ArrayList<>()).add(x);
    }
    for (var e : g1.entrySet()) {
      Collections.sort(e.getValue());
    }
    for (var e : g2.entrySet()) {
      Collections.sort(e.getValue());
    }
    int ans = 0;
    for (int[] building : buildings) {
      int x = building[0], y = building[1];
      List<Integer> l1 = g1.get(x);
      List<Integer> l2 = g2.get(y);
      if (l2.get(0) < x && x < l2.get(l2.size() - 1) && l1.get(0) < y &&
          y < l1.get(l1.size() - 1)) {
        ans++;
      }
    }
    return ans;
  }
}

Video Solution

Watch the video walkthrough for Count Covered Buildings



Algorithms:

Sorting

Data Structures:

ArrayHash Table

Subscribe to Scale Engineer newsletter

Learn about System Design, Software Engineering, and interview experiences every week.

No spam, unsubscribe at any time.