LeetCode算法学习之--二分查找--供暖器

656 阅读3分钟

大家好今天给大家分享下一道 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的区间是【0max(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)
*/

1.png

解法二:迭代法

代码借鉴 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)
*/

2.png

总结

这道题是考察如果把题目转化成有序的数组,从而可以进行二分法的应用 或者迭代的应用

大家可以看看我分享的一个专栏(前端搞算法)里面有更多关于算法的题目的分享,希望能够帮到大家,我会尽量保持每天晚上更新,如果喜欢的麻烦帮我点个赞,十分感谢

文章内容目的在于学习讨论与分享学习算法过程中的心得体会,文中部分素材来源网络,如有侵权,请联系删除,邮箱 182450609@qq.com