大家好今天给大家分享下一道 LeetCode 中等难度 的题目[供暖器]
这里主要是分享思路和注释,供大家更好的理解题目解法,代码部分是参考LeetCode 转写成javascript 代码,
题目
冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。
在加热器的加热半径范围内的每个房屋都可以获得供暖。
现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。
说明:所有供暖器都遵循你的半径标准,加热的半径也一样。
示例 1: 输入: houses = [1,2,3], heaters = [2] 输出: 1 解释: 仅在位置2上有一个供暖器。如果我们将加热半径设为1,那么所有房屋就都能得到供暖。 示例 2: 输入: houses = [1,2,3,4], heaters = [1,4] 输出: 1 解释: 在位置1, 4上有两个供暖器。我们需要将加热半径设为1,这样所有房屋就都能得到供暖。 示例 3: 输入:houses = [1,5], heaters = [2] 输出:3 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/heaters 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
1.假设半径为r,每个heater覆盖面积是 [heater-r,heater+r]
2.所有的heater必须覆盖所有House
解法
1.二分法
2.迭代法
解法一:二分法
思路
1.把houses 和 heaters 变成升序数列才能符合二分查找的条件
2.最近半径radius的区间是【0,max(houses[houses.length-1],heater[heaters.length-1])】
3.迭代买个heaters,求出它的覆盖面积,如果最终所有house 都被覆盖,则说明找到答案。
4.使用二分查找中的中的寻找最左值 来寻求最小值
*/
var findRadius = function (houses, heaters) {
// 先把houses 和 heasters 变成升序,才能符合二分查找的条件
houses.sort((a, b) => a - b);
heaters.sort((a, b) => a - b);
// 求出radius的区间
let left = 0;
let right = Math.max(heaters[heaters.length - 1], houses[houses.length - 1]);
// 使用二分查找中的寻求最左极值的方法来查找 最小的radius
while (left <= right) {
// 这里的mid 相当于半径
const radius = Math.floor(left + (right - left) / 2);
if (pass(radius)) {
right = radius - 1;
} else {
left = radius + 1;
}
}
return left;
function pass(radius) {
let last_next_house_index = 0;
for (const heater of heaters) {
// 求出覆盖的最左的house的位置
const left_house_index = bisect_left(houses, heater - radius);
// 如果位置大于上一轮中的最右的边的house的下一个house 的index,则说明没有全覆盖
if (left_house_index > last_next_house_index) {
return false;
}
//求出覆盖的最右的house的下一个house的index(这里有点绕,可以看下bisect_right的方法 就可以明白意思)
const next_house_index = bisect_right(houses, heater + radius);
// 记录下最右边的位置
last_next_house_index = next_house_index;
// 如果最右边的位置的index已经大于houses的length 说明已经全覆盖
if (next_house_index >= houses.length) {
return true;
}
}
// 如果heaters 都遍历完了都还没有覆盖完,radius 也是不合格的
return false;
}
function bisect_left(nums, target) {
let l = 0;
let r = nums.length - 1;
while (l <= r) {
const mid = Math.floor(l + (r - l) / 2);
if (nums[mid] >= target) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return l;
}
function bisect_right(nums, target) {
let l = 0;
let r = nums.length - 1;
while (l <= r) {
const mid = Math.floor(l + (r - l) / 2);
if (nums[mid] <= target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return l;
}
};
/* 复杂度
时间 O(logN*logK) N为radius 区间数组的个数, k为houses的个数
空间 O(1)
*/
解法二:迭代法
代码借鉴 leetcode-cn.com/problems/he…
思考
1.一次遍历 house , 依次求得每个house对应最近的那个heater的距离 dis
2.获取最大的dis
*/
var findRadius = function (houses, heaters) {
houses.sort((a, b) => a - b);
heaters.sort((a, b) => a - b);
let res = 0;
let i = 0;
for (const house of houses) {
// 当前heater距离house的绝对距离
let currentHeaterDis = Math.abs(heaters[i] - house);
// 下一个heater距离house的绝对距离
let nextHeaterDis = Math.abs(heaters[i + 1] - house);
// 如果当前的比下一个大,则需要取下一个heater来比较距离
while (i < heaters.length - 1 && currentHeaterDis >= nextHeaterDis) {
i++;
//重新計算距離
currentHeaterDis = Math.abs(heaters[i] - house);
nextHeaterDis = Math.abs(heaters[i + 1] - house);
}
res = Math.max(res, currentHeaterDis);
}
return res;
};
/* 复杂度
时间 O(n) n为house 的个数
空间 O(1)
*/
总结
这道题是考察如果把题目转化成有序的数组,从而可以进行二分法的应用 或者迭代的应用
大家可以看看我分享的一个专栏(前端搞算法)里面有更多关于算法的题目的分享,希望能够帮到大家,我会尽量保持每天晚上更新,如果喜欢的麻烦帮我点个赞,十分感谢
文章内容目的在于学习讨论与分享学习算法过程中的心得体会,文中部分素材来源网络,如有侵权,请联系删除,邮箱 182450609@qq.com