1. 问题背景与需求分析
供暖器问题是一个经典的算法优化场景,它模拟了现实中的供暖设备布置问题。给定房屋和供暖器的位置,我们需要找到供暖器的最小加热半径,使得所有房屋都能被至少一个供暖器覆盖。
这个问题看似简单,但隐藏着几个关键挑战:
- 房屋和供暖器的分布是无序的,需要先排序才能高效处理
- 加热半径的确定需要同时考虑左右两侧最近的供暖器
- 最小化最大半径的要求提示我们需要用优化算法而非暴力搜索
在实际工程中,类似场景比比皆是:比如基站信号覆盖范围优化、物流仓库的配送点选址、城市消防站布局等。这类问题的共同特点是需要在满足全覆盖的前提下,最小化资源投入。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 暴力解法与性能瓶颈
最直观的解法是对于每个房屋,找出离它最近的供暖器,然后记录所有房屋对应距离的最大值:
java复制public int findRadius(int[] houses, int[] heaters) {
int res = 0;
for (int house : houses) {
int minDist = Integer.MAX_VALUE;
for (int heater : heaters) {
minDist = Math.min(minDist, Math.abs(house - heater));
}
res = Math.max(res, minDist);
}
return res;
}
这种解法的时间复杂度是O(M*N),其中M是房屋数量,N是供暖器数量。当数据量达到10^4级别时,这种解法就会超时(约需要10^8次操作)。这引出了我们对更高效算法的需求。
提示:在实际面试中,即使你能想到更优解法,也应该先提出暴力解法并分析其复杂度,这展示了你的解题思路完整性。
3. 二分查找优化思路
3.1 排序预处理
观察发现,如果供暖器是有序排列的,我们可以用二分查找快速定位每个房屋的最近供暖器:
java复制Arrays.sort(heaters); // 只需排序一次
排序的时间复杂度是O(N log N),之后每次查询可以降到O(log N),整体复杂度优化为O((M+N) log N)。
3.2 二分查找实现
对于每个房屋,在有序供暖器数组中:
- 用二分查找找到第一个不小于房屋位置的供暖器
- 比较这个供暖器和前一个供暖器哪个更近
java复制private int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return left; // 返回插入位置
}
3.3 处理边界情况
需要特别注意两种边界情况:
- 当房屋位置小于所有供暖器时(返回的index=0)
- 当房屋位置大于所有供暖器时(返回的index=heaters.length)
java复制int index = binarySearch(heaters, house);
int dist1 = index == 0 ? Integer.MAX_VALUE : house - heaters[index-1];
int dist2 = index == heaters.length ? Integer.MAX_VALUE : heaters[index] - house;
res = Math.max(res, Math.min(dist1, dist2));
4. 二分答案解法进阶
4.1 解法思路
另一种思路是直接对答案(加热半径)进行二分查找:
- 确定可能的最小和最大半径(0和最远房屋距离)
- 对于每个候选半径,检查是否能覆盖所有房屋
- 根据检查结果调整搜索范围
java复制public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int left = 0, right = Math.max(
houses[houses.length-1] - heaters[0],
heaters[heaters.length-1] - houses[0]
);
while (left < right) {
int mid = left + (right - left) / 2;
if (canCover(houses, heaters, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
4.2 覆盖检查实现
检查函数需要验证给定半径是否能覆盖所有房屋:
java复制private boolean canCover(int[] houses, int[] heaters, int radius) {
int i = 0, j = 0;
while (i < houses.length && j < heaters.length) {
if (Math.abs(houses[i] - heaters[j]) <= radius) {
i++; // 当前房屋已被覆盖
} else {
j++; // 尝试下一个供暖器
}
}
return i == houses.length;
}
这种解法的时间复杂度是O((M+N) log D),其中D是最大可能距离。对于极端分布的数据(如房屋和供暖器都集中在两端),这种解法可能更优。
5. 双指针解法对比
5.1 解法实现
还有一种结合了排序和双指针的解法,同样高效:
java复制public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int res = 0;
int i = 0;
for (int house : houses) {
while (i < heaters.length - 1 &&
Math.abs(heaters[i+1] - house) <= Math.abs(heaters[i] - house)) {
i++;
}
res = Math.max(res, Math.abs(heaters[i] - house));
}
return res;
}
5.2 性能分析
这种方法的时间复杂度是O(M log M + N log N + M + N),即排序加单次遍历。在实际测试中,这种方法往往是最快的,因为:
- 避免了二分查找的常数因子开销
- 内存访问模式更连续,缓存友好
- 代码更简洁,分支预测更友好
6. 工程实践中的优化技巧
6.1 输入数据预处理
在实际工程中,我们可能还需要:
- 检查输入数组是否已排序,避免不必要的排序
- 处理空输入或单元素输入的边界情况
- 对超大数组考虑内存映射文件处理
java复制if (houses.length == 0) return 0;
if (heaters.length == 0) return Integer.MAX_VALUE; // 根据业务需求决定
6.2 算法选择策略
根据数据特征选择最优算法:
- 房屋数量远大于供暖器数量 → 二分查找解法
- 数据分布均匀 → 双指针解法
- 需要频繁查询 → 预处理建立搜索结构
6.3 数值计算优化
在性能关键场景可以:
- 使用位运算代替除法
- 提前计算并缓存重复使用的值
- 使用更紧凑的数据结构减少内存占用
java复制int mid = (left + right) >>> 1; // 无符号右移代替除法
7. 测试用例设计
全面的测试用例应该包括:
java复制@Test
public void testFindRadius() {
// 常规情况
assertEquals(1, findRadius(new int[]{1,2,3}, new int[]{2}));
// 供暖器在房屋两侧
assertEquals(3, findRadius(new int[]{1,5}, new int[]{2,8}));
// 单个房屋
assertEquals(0, findRadius(new int[]{1}, new int[]{1}));
// 大数测试
assertEquals(999999999, findRadius(
new int[]{1,1000000000},
new int[]{500000000}));
// 重复位置
assertEquals(0, findRadius(
new int[]{1,1,1,1},
new int[]{1}));
}
8. 算法扩展思考
这个问题可以延伸出多个变种:
- 加权版本:不同供暖器有不同的安装成本
- 移动供暖器:在给定总移动距离限制下优化覆盖
- 动态维护:支持房屋和供暖器的动态增删
- 多维空间:将问题扩展到二维或三维空间
对于移动供暖器变种,可以结合贪心算法:
java复制public int findMinMoveRadius(int[] houses, int[] heaters, int maxMove) {
// 先排序
// 使用双指针在移动限制内寻找最优解
// 可能需要结合动态规划
}
在工业界的实际系统中,这类问题通常会结合机器学习预测房屋的热量需求,实现动态调整供暖半径的智能系统。
