LeetCode 475. 供暖器
题目描述
✅ 475. 供暖器


题意分析
房屋和供暖器位于一条直线上,所有供暖器使用相同的加热半径。每栋房屋只需落在至少一台供暖器的覆盖范围内,求能覆盖全部房屋的最小半径。
一台供暖器可以覆盖任意多栋房屋,不存在逐个配对或容量限制。因此每栋房屋可以独立选择离它最近的供暖器,再综合这些房屋各自的半径需求。
解法:排序 + 二分
核心思路
[!blue]
对一栋房屋来说,至少需要的半径就是它到最近供暖器的距离。把所有房屋的这个距离取最大值
R:半径小于R,必然漏掉达到最大需求的房屋;半径等于R,每栋房屋都能被自己的最近供暖器覆盖。所以这个最大值恰好是最优答案。把供暖器位置排序,对当前房屋用下界二分查找第一个不小于它的位置
idx。左侧供暖器中,最靠右的idx - 1最近;右侧供暖器中,最靠左的idx最近,其余供暖器只会更远,因此只需比较这两个邻居。下界二分维护半开区间
[left, right)。若heaters[mid] >= house,答案可能就是mid或在它左侧,让right = mid;否则mid及左侧都不够大,让left = mid + 1。两端相遇时就是第一个满足条件的位置,也可能等于数组长度。
idx = 0时只有右侧候选,idx = heaters.length时只有左侧候选,其他情况取两侧距离的较小值。房屋恰好与供暖器重合时,这个最近距离自然为0。
解题步骤
- 将
heaters原地升序排序,答案初始化为0。- 对每栋房屋查找第一台位置不小于它的供暖器,得到下界
idx。- 根据
idx是否位于两端,计算现有左、右邻居的距离,取最近距离。- 用该房屋的最近距离更新全局最大值。
- 返回最终半径。题目保证至少有一台供暖器,因此每栋房屋总有一侧候选可用。
代码实现
class Solution {
// 将供暖器位置排序后,对任意房屋,最近供暖器只可能在插入位置的左侧或右侧。
public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(heaters);
int answer = 0;
for (int house : houses) {
int idx = lowerBound(heaters, house);
int distance;
// 插入位置可能在两端,只访问实际存在的邻居
if (idx == 0) {
distance = heaters[0] - house;
} else if (idx == heaters.length) {
distance = house - heaters[heaters.length - 1];
} else {
int leftDistance = house - heaters[idx - 1];
int rightDistance = heaters[idx] - house;
distance = Math.min(leftDistance, rightDistance);
}
// 每栋先求最近距离,再取所有房屋的最大需求
answer = Math.max(answer, distance);
}
return answer;
}
private int lowerBound(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
import "sort"
func findRadius(houses []int, heaters []int) int {
// 将供暖器位置排序后,对任意房屋,最近供暖器只可能在插入位置的左侧或右侧。
sort.Ints(heaters)
answer := 0
for _, house := range houses {
idx := lowerBound475(heaters, house)
distance := 0
// 插入位置可能在两端,只访问实际存在的邻居
if idx == 0 {
distance = heaters[0] - house
} else if idx == len(heaters) {
distance = house - heaters[len(heaters)-1]
} else {
leftDistance := house - heaters[idx-1]
rightDistance := heaters[idx] - house
if leftDistance < rightDistance {
distance = leftDistance
} else {
distance = rightDistance
}
}
// 每栋先求最近距离,再取所有房屋的最大需求
if distance > answer {
answer = distance
}
}
return answer
}
func lowerBound475(nums []int, target int) int {
left, right := 0, len(nums)
for left < right {
mid := left + (right-left)/2
if nums[mid] >= target {
right = mid
} else {
left = mid + 1
}
}
return left
}
复杂度分析
- 时间复杂度:$O((h+m)\log(h+1))$,h 为供暖器数、m 为房屋数。
- 空间复杂度:查询部分 $O(1)$,另计供暖器排序的辅助空间。
关键点总结
[!green]
- 每栋房屋对供暖器取最小距离,不同房屋之间再取最大需求。
- 供暖器排序后,距离最近者只可能是下界位置及它的前驱。
- 下界允许返回数组长度,这个哨兵位置表示所有供暖器都在房屋左侧。
易错点总结
[!yellow]
- 下界可能为
0或供暖器数量,需要先处理边界,不能直接同时访问idx - 1和idx。- 只看左侧或只看右侧,可能漏掉另一侧更近的供暖器。
- 不要把房屋与供暖器一一配对,同一台供暖器可以同时满足多栋房屋。
- 全局答案取最大最近距离;取最小值只能保证某栋房屋被覆盖,不能保证全部房屋。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 35. 搜索插入位置 | 简单 | 排序供暖器后,对每所房子查下界及其前驱,即可找到最近供暖器。 |
| 849. 到最近的人的最大距离 | 中等 | 同样取到最近已占位置的距离,原题选择最远空座位,本题要让所有房子都覆盖,取各最近距离的最大值。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!