Facebook Pixel

475. Heaters

Problem Description

You need to warm all houses using heaters with a fixed radius. Each heater can warm any house that falls within its warming radius on both sides.

You're given two arrays:

  • houses: positions of houses on a horizontal line
  • heaters: positions of heaters on a horizontal line

Your task is to find the minimum radius that all heaters must have to ensure every house is covered by at least one heater. All heaters will use the same radius value.

A house at position x is warmed by a heater at position y with radius r if the distance between them is at most r, meaning |x - y| <= r.

For example:

  • If a heater is at position 5 with radius 2, it can warm houses at positions 3, 4, 5, 6, and 7
  • If houses are at [1, 2, 3] and heaters are at [2], a radius of 1 would warm all houses since the heater at position 2 can reach houses at positions 1, 2, and 3

The solution uses binary search on the radius value combined with a greedy checking mechanism. After sorting both arrays, it searches for the smallest radius between 0 and 10^9. For each candidate radius mid, it checks if all houses can be covered by using a two-pointer technique: advancing through houses and heaters to verify if each house falls within the range [heater_position - radius, heater_position + radius] of some heater.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing this as an optimization problem where we need to find the minimum value (radius) that satisfies a condition (all houses are covered). This pattern naturally suggests binary search on the answer.

Why binary search works here? Consider that if a radius r can warm all houses, then any radius larger than r will also work. Conversely, if radius r cannot warm all houses, then any smaller radius will also fail. This creates a monotonic property: there exists a threshold radius where all values below it fail and all values at or above it succeed.

The challenge then becomes: given a specific radius, how do we efficiently check if all houses can be warmed? After sorting both arrays, we can use a greedy two-pointer approach. We process houses from left to right and for each house, we find if it can be covered by the current heater. If a house is too far to the left of the current heater (outside its range), we know this radius is insufficient. If a house is too far to the right, we move to the next heater and try again.

The sorting is crucial because it allows us to process houses and heaters in order, ensuring we don't miss any coverage gaps. Once sorted, we can confidently say that if we can't cover a house with the current or any subsequent heater, then the radius is too small.

The range for binary search [0, 10^9] is chosen to cover all possible distances since house and heater positions are typically bounded by this range in the problem constraints. We keep narrowing down this range until we find the exact minimum radius needed.

Solution Implementation

1class Solution:
2    def findRadius(self, houses: List[int], heaters: List[int]) -> int:
3        houses.sort()
4        heaters.sort()
5
6        def feasible(radius):
7            house_idx = 0
8            heater_idx = 0
9
10            while house_idx < len(houses):
11                if heater_idx >= len(heaters):
12                    return False
13
14                min_coverage = heaters[heater_idx] - radius
15                max_coverage = heaters[heater_idx] + radius
16
17                if houses[house_idx] < min_coverage:
18                    return False
19                if houses[house_idx] > max_coverage:
20                    heater_idx += 1
21                else:
22                    house_idx += 1
23
24            return True
25
26        left, right = 0, int(1e9)
27        first_true_index = -1
28
29        while left <= right:
30            mid = (left + right) // 2
31            if feasible(mid):
32                first_true_index = mid
33                right = mid - 1
34            else:
35                left = mid + 1
36
37        return first_true_index
38
1class Solution {
2    public int findRadius(int[] houses, int[] heaters) {
3        Arrays.sort(houses);
4        Arrays.sort(heaters);
5
6        int left = 0, right = 1000000000;
7        int firstTrueIndex = -1;
8
9        while (left <= right) {
10            int mid = left + (right - left) / 2;
11            if (feasible(houses, heaters, mid)) {
12                firstTrueIndex = mid;
13                right = mid - 1;
14            } else {
15                left = mid + 1;
16            }
17        }
18
19        return firstTrueIndex;
20    }
21
22    private boolean feasible(int[] houses, int[] heaters, int radius) {
23        int houseIdx = 0;
24        int heaterIdx = 0;
25
26        while (houseIdx < houses.length) {
27            if (heaterIdx >= heaters.length) {
28                return false;
29            }
30
31            int minCoverage = heaters[heaterIdx] - radius;
32            int maxCoverage = heaters[heaterIdx] + radius;
33
34            if (houses[houseIdx] < minCoverage) {
35                return false;
36            }
37            if (houses[houseIdx] > maxCoverage) {
38                heaterIdx++;
39            } else {
40                houseIdx++;
41            }
42        }
43
44        return true;
45    }
46}
47
1class Solution {
2public:
3    int findRadius(vector<int>& houses, vector<int>& heaters) {
4        sort(houses.begin(), houses.end());
5        sort(heaters.begin(), heaters.end());
6
7        auto feasible = [&](int radius) {
8            int houseIdx = 0;
9            int heaterIdx = 0;
10
11            while (houseIdx < (int)houses.size()) {
12                if (heaterIdx >= (int)heaters.size()) {
13                    return false;
14                }
15
16                int minCoverage = heaters[heaterIdx] - radius;
17                int maxCoverage = heaters[heaterIdx] + radius;
18
19                if (houses[houseIdx] < minCoverage) {
20                    return false;
21                }
22                if (houses[houseIdx] > maxCoverage) {
23                    heaterIdx++;
24                } else {
25                    houseIdx++;
26                }
27            }
28
29            return true;
30        };
31
32        int left = 0, right = 1000000000;
33        int firstTrueIndex = -1;
34
35        while (left <= right) {
36            int mid = left + (right - left) / 2;
37            if (feasible(mid)) {
38                firstTrueIndex = mid;
39                right = mid - 1;
40            } else {
41                left = mid + 1;
42            }
43        }
44
45        return firstTrueIndex;
46    }
47};
48
1function findRadius(houses: number[], heaters: number[]): number {
2    houses.sort((a, b) => a - b);
3    heaters.sort((a, b) => a - b);
4
5    const feasible = (radius: number): boolean => {
6        let houseIdx = 0;
7        let heaterIdx = 0;
8
9        while (houseIdx < houses.length) {
10            if (heaterIdx >= heaters.length) {
11                return false;
12            }
13
14            const minCoverage = heaters[heaterIdx] - radius;
15            const maxCoverage = heaters[heaterIdx] + radius;
16
17            if (houses[houseIdx] < minCoverage) {
18                return false;
19            }
20            if (houses[houseIdx] > maxCoverage) {
21                heaterIdx++;
22            } else {
23                houseIdx++;
24            }
25        }
26
27        return true;
28    };
29
30    let left = 0;
31    let right = 1000000000;
32    let firstTrueIndex = -1;
33
34    while (left <= right) {
35        const mid = Math.floor((left + right) / 2);
36        if (feasible(mid)) {
37            firstTrueIndex = mid;
38            right = mid - 1;
39        } else {
40            left = mid + 1;
41        }
42    }
43
44    return firstTrueIndex;
45}
46

Solution Approach

The solution uses binary search on the answer space combined with a greedy validation function.

Step 1: Sorting Both arrays are sorted to enable efficient checking using a two-pointer technique.

Step 2: Binary Search Setup

  • left = 0 (minimum possible radius)
  • right = 10^9 (maximum possible radius)
  • first_true_index = -1 (tracks the answer)

Step 3: Feasible Function The feasible(radius) function determines if all houses can be covered with the given radius. This creates a boolean pattern:

radius:   0    1    2    3    4    5    ...
feasible: false false false false false true ...

The checking logic uses two pointers:

  • For each heater, its coverage range is [heater - radius, heater + radius]
  • If a house is left of all heater ranges, the radius is too small
  • If a house is beyond current heater's range, try the next heater

Step 4: Binary Search Template

left, right = 0, int(1e9)
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1  # Try smaller radius
    else:
        left = mid + 1   # Need larger radius

return first_true_index

Time Complexity: O((m + n) log(m + n) + (m + n) log(10^9)) where m is houses, n is heaters. Space Complexity: O(1) excluding sorting space.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through with houses = [1, 5, 15] and heaters = [6, 10].

Step 1: Sort both arrays

  • Houses: [1, 5, 15] (already sorted)
  • Heaters: [6, 10] (already sorted)

Step 2: Binary search with template

  • left = 0, right = 10^9, first_true_index = -1

Key iterations (skipping large radius checks):

When mid = 5:

  • Check feasible(5):
    • Heater 6 covers range [1, 11]
    • House 1: 1 ≥ 1 and 1 ≤ 11 → covered ✓
    • House 5: 5 ≥ 1 and 5 ≤ 11 → covered ✓
    • House 15: 15 > 11 → try next heater
    • Heater 10 covers range [5, 15]
    • House 15: 15 ≥ 5 and 15 ≤ 15 → covered ✓
    • Result: True
  • first_true_index = 5, right = 4

When mid = 2 (in range [0, 4]):

  • Check feasible(2):
    • Heater 6 covers range [4, 8]
    • House 1: 1 < 4 → not covered!
    • Result: False
  • left = 3

When mid = 4 (in range [3, 4]):

  • Check feasible(4):
    • Heater 6 covers range [2, 10]
    • House 1: 1 < 2 → not covered!
    • Result: False
  • left = 5

Loop ends: left = 5 > right = 4

Answer: first_true_index = 5

Verification:

  • Heater 6 with radius 5: covers [1, 11] → houses 1 and 5
  • Heater 10 with radius 5: covers [5, 15] → house 15
  • All houses covered with minimum radius 5.

Time and Space Complexity

Time Complexity: O((m + n) * log(m + n) + (m + n) * log(max_distance))

  • Sorting houses takes O(m * log(m)) where m is the number of houses
  • Sorting heaters takes O(n * log(n)) where n is the number of heaters
  • Binary search runs for O(log(max_distance)) iterations, where max_distance is the search space from 0 to 10^9
  • Each binary search iteration calls the check() function, which takes O(m + n) time in the worst case as it iterates through all houses and potentially all heaters
  • Overall: O(m * log(m) + n * log(n) + (m + n) * log(10^9))
  • Since log(10^9) ≈ 30 is a constant, and typically m + n dominates the sorting complexity, this simplifies to O((m + n) * log(m + n) + (m + n) * log(max_distance))

Space Complexity: O(1)

  • The sorting operations are done in-place (Python's sort uses Timsort which requires O(min(m, n)) auxiliary space in worst case, but typically O(1) for already sorted or small arrays)
  • The algorithm only uses a constant amount of extra variables (i, j, left, right, mid, mi, mx, r)
  • No additional data structures are created
  • Therefore, the space complexity is O(1) excluding the space used by sorting

Common Pitfalls

1. Using Wrong Binary Search Template Variant

A common mistake is using while left < right with right = mid instead of the standard template.

Incorrect:

while left < right:
    mid = (left + right) // 2
    if feasible(mid):
        right = mid
    else:
        left = mid + 1
return left

Correct (template-compliant):

first_true_index = -1
while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1
return first_true_index

The template approach explicitly tracks the answer and handles edge cases consistently.

2. Incorrect Coverage Check Logic

Not checking both boundaries of the heater's coverage range.

Incorrect:

def feasible(radius):
    for heater in heaters:
        while house_idx < len(houses) and houses[house_idx] <= heater + radius:
            house_idx += 1
    return house_idx == len(houses)  # Missing left boundary check!

Correct:

if houses[house_idx] < min_coverage:
    return False  # House is too far left of all heaters

3. Forgetting to Sort Arrays

The two-pointer feasibility check requires sorted arrays.

# Always sort both arrays first
houses.sort()
heaters.sort()

4. Not Handling Edge Cases

When all houses are covered by the first heater, or when arrays have different sizes.

The feasible function handles these automatically with proper two-pointer logic.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which type of traversal does breadth first search do?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More