【LeetCode】475. Heaters (java实现)

强颜欢笑 提交于 2019-11-27 05:16:39

原题链接

https://leetcode.com/problems/heaters/

原题

Winter is coming! Your first job during the contest is to design a standard heater with fixed warm radius to warm all the houses.

Now, you are given positions of houses and heaters on a horizontal line, find out minimum radius of heaters so that all houses could be covered by those heaters.

So, your input will be the positions of houses and heaters seperately, and your expected output will be the minimum radius standard of heaters.

Note:

Numbers of houses and heaters you are given are non-negative and will not exceed 25000.
Positions of houses and heaters you are given are non-negative and will not exceed 10^9.
As long as a house is in the heaters' warm radius range, it can be warmed.
All the heaters follow your radius standard and the warm radius will the same.

Example 1:

Input: [1,2,3],[2]
Output: 1
Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, then all the houses can be warmed.

Example 2:

Input: [1,2,3,4],[1,4]
Output: 1
Explanation: The two heater was placed in the position 1 and 4. We need to use radius 1 standard, then all the houses can be warmed.

题目要求

冬天到了,需要利用加热器为房屋取暖。多个房屋和多个取暖器都在一条直线上,每个取暖器只能加热一段范围内的房屋。给定两个数组,分别表示房屋的坐标和取暖器的坐标,如果要求每个房屋都能够被加热,求出取暖器最小的加热范围半径。比如房屋坐标为[1,2,3],取暖器坐标为[2],那么取暖器的加热范围半径为1就可以为所有房屋取暖了。

解法

解法一:超时的做法。将所有的取暖器放入一个map中,然后对每个house,尝试在其坐标+j和-j(从1开始递增)的位置寻找取暖器(在map中搜索),如果找到,那么这个j就是这个house的最小半径。最后,对每个house的最小半径再求最小值。这种做法虽然简单容易理解,但是性能并不高,尤其在数字较大时,很容易超时,因为每个house都是递增+j-j的搜索取暖器。

public int findRadius(int[] houses, int[] heaters) {
    Arrays.sort(houses);
    Arrays.sort(heaters);
    
    int posMin = houses[0] > heaters[0] ? heaters[0] : houses[0];
    int posMax = houses[houses.length - 1] > heaters[heaters.length - 1] ? houses[houses.length - 1] : heaters[heaters.length - 1];
    
    HashMap<Integer, Boolean> heaterMap = new HashMap<Integer, Boolean>();
    // add to HashMap, to increase search speed.
    for (int i = 0; i < heaters.length; i++) {
        heaterMap.put(heaters[i], true);
    }
    
    int max = 0;
    for (int i = 0; i < houses.length; i++) {
        int pos = houses[i];
        for (int j = 0; pos - j >= posMin || pos + j <= posMax; j++) {
            // try to search left or right, onece found, judge max and break;
            if (heaterMap.containsKey(pos - j)) {
                max = (max < j? j : max);
                break;
            }
            if (heaterMap.containsKey(pos + j)) {
                max = (max < j? j : max);
                break;
            }
        }
    }
    
    return max;
}

解法二:在方法1的基础上做了优化。既然是对每个house都+j-j的搜索,其实就是尝试在每个house的左边和右边找到最近的取暖器,然后在左边和右边的取暖器中找出个最小半径。最后再在所有house的最小半径中,找出一个最大值,就可以保证所有house都能取暖了。因此,这种解法,就是创建两个新数组,表示每个house的左边和右边取暖器的坐标,这里有一个优化(我认为是一个优化,不过也可以不用这种方式),就是坐标数组里保存的都是真实坐标+1,这样,0就可以表示house的左边或者右边没有取暖器了。

public int findRadius(int[] houses, int[] heaters) {
    Arrays.sort(houses);
    Arrays.sort(heaters);

    int left = 0, right = heaters.length - 1;
    int[] lefts = new int[houses.length];
    int[] rights = new int[houses.length];
    for (int i = 0; i < houses.length; i++) {
        while (left < heaters.length && heaters[left] <= houses[i]) {
            lefts[i] = left + 1;
            left++;
        }
        left = lefts[i] != 0 ? (lefts[i] - 1) : 0;
        while (right >= left && heaters[right] >= houses[i]) {
            rights[i] = right + 1;
            right--;
        }
        right = heaters.length - 1;
    }
    
    int ret = 0;
    for (int i = 0; i < houses.length; i++) {
        int min = Integer.MAX_VALUE;
        if (lefts[i] != 0) {
            min = Math.min(houses[i] - heaters[lefts[i] - 1], min);
        }
        if (rights[i] != 0) {
            min = Math.min(heaters[rights[i] - 1] - houses[i], min);
        }
        ret = Math.max(min, ret);
    }

    System.out.println(ret);
    return ret;
}

Top解法:这是Discuss中的top解法。先将取暖器数组排序,在遍历所有house,对每个house,在取暖器数组中进行binary search,如果命中,则说明取暖器位置和house位置重合,这个house的最小半径为0;如果没有命中,则使用返回的index,将index左边和右边的取暖器坐标与house坐标求差值,找出这个house最小半径。说白了,也是在查找house的最近左右取暖器,只是这种解法,非常清晰简单,令人佩服。

public int findRadius(int[] houses, int[] heaters) {
    Arrays.sort(heaters);
    int result = 0;
    
    for (int house : houses) {
        int index = Arrays.binarySearch(heaters, house);
        if (index < 0) {
            index = ~index;
            int dist1 = index - 1 >= 0 ? house - heaters[index - 1] : Integer.MAX_VALUE;
            int dist2 = index < heaters.length ? heaters[index] - house : Integer.MAX_VALUE;
            
            result = Math.max(result, Math.min(dist1, dist2));
        }
    }
    
    return result;
}

测试用例:

public static void main(String[] args) {
    Solution s = new Solution();
    {
        int[] house = {1,2,3,4};
        int[] heaters = {1,4};
        assert(s.findRadius(house, heaters) == 1);
    }
    {
        int[] house = {1,5};
        int[] heaters = {10};
        assert(s.findRadius(house, heaters) == 9);
    }
    {
        int[] house = {1,2,3};
        int[] heaters = {2};
        assert(s.findRadius(house, heaters) == 1);
    }
    {
        int[] house = {7,9, 1};
        int[] heaters = {2};
        assert(s.findRadius(house, heaters) == 7);
    }
    {
        int[] house = {282475249,622650073,984943658,144108930,470211272,101027544,457850878,458777923};
        int[] heaters = {823564440,115438165,784484492,74243042,114807987,137522503,441282327,16531729,823378840,143542612};
        assert(s.findRadius(house, heaters) == 161834419);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!