Leetcode 475. Heaters

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fantasiasango/article/details/80691458



思路:

这题虽然是Easy标签,但是一点也不easy啊。。一开始说能不能按照meeting interval那个题,做线段扫描。搞了TreeMap结果发现并不能行,原因是我们想要的是house-heater的连线,然而把所有的点放入Map里面之后,并没法判断给定一个位置,这个位置到底是既有house又有heater,还是只有house,或者只有heater,当然我们可以有1,2,3去代表三种状态,然后遍历的时候就需要各种判断,比如上个最近的heater位置,和最近的house位置等等,而且最后结束的时候还得判断最后一个点是不是house,总之就是有很多if statement。

看了别人的思路,的确,以上那么多if,其实就干了一件事情:判断一个点和最近heater的距离。那既然是最近heater,就可能是左右中的一个,也有可能只有左,或者右。于是按照别人思路,用int[] lefts和int[] rights来表示排序后heater数组中紧邻house中元素的heater位置,比如对house[1],lefts[1]表示heater中位置小于等于house[1]的元素index, rights[1]表示heater中位置大于或者等于house[1]的heater index。这样初始化之后,直接再loop一遍house,判断左右中的距离最小值,然后更新result = Math.max(result, Math.min(leftDist, rightDist))就可以了。在初始化lefts和rights的时候可以存index + 1,这样就可以判断有没有找到这样的值。比如[1,2,3] 和 [4],这样对于所有的位置,lefts都会是0,因为找不到heater位置使得heater[left[i]] < house[i]. 原文中对houses也排序了,博主认为不用,就去掉了:

class Solution { // 1014 ms
   public int findRadius(int[] houses, int[] heaters) {
    Arrays.sort(heaters);

    int[] lefts = new int[houses.length];
    int[] rights = new int[houses.length];
    for (int i = 0; i < houses.length; i++) {
        int left = 0, right = heaters.length - 1;
        while (left < heaters.length && heaters[left] <= houses[i]) {
            lefts[i] = left + 1;
            left++;
        }   
        while (right >= 0 && heaters[right] >= houses[i]) {
            rights[i] = right + 1;
            right--;
        }
        
    }

    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);
    }

    return ret;
    }
}

优化:

其实根本不用存lefts和rights,每次计算出当前house对应的left和right直接更新结果即可:

class Solution { // 699ms
    public int findRadius(int[] houses, int[] heaters) {
        Arrays.sort(heaters);
        int res = 0;
        
        for (int i = 0; i < houses.length; i++) {
            int left = 0, right = 0;
            int start = 0, end = heaters.length - 1;
            while (start < heaters.length && heaters[start] <= houses[i]) {
                left = start + 1;
                start++;
            }
            start = left != 0 ? (left - 1) : 0;
            while(end >= start && heaters[end] >= houses[i]) {
                right = end + 1;
                end--;
            }
            
            int min = Integer.MAX_VALUE;
            if (left != 0) {
                min = Math.min(min, houses[i] - heaters[left - 1]);
            }
            if (right != 0) {
                min = Math.min(min, heaters[right - 1] - houses[i]);
            }
            res = Math.max(res, min);
        }
        
        return res;
    }
}

思路2:

排序heaters数组,然后用二分法。trick点在于,二分法当没找到该元素时候,返回值为负数,取反变为正数时候恰为数组中该元素应该插入的位置。比如[2,3,4]查找0,则会返回-1,因为应该插入在0位置,而~0 = 111...11111 = -1. 于是对于每个house值,我们查找值为正数,说明该house有heater,什么都不做;否则,找到该插入位置,重新计算左右距离。比如返回值为-1,说明heater中所有元素都比当前house元素小,那么leftDist应该为无穷大(默认值),rightDist = heater[0] - house, res = Math.max(Math.min(leftDist, rightDist), res).

class Solution { // 26 ms
    public int findRadius(int[] houses, int[] heaters) {
        Arrays.sort(heaters);
        int res = 0;
        for (int i = 0; i < houses.length; i++) {
            int index = Arrays.binarySearch(heaters, houses[i]);
            if (index < 0) {
                index = ~index;
                int left = index - 1 >= 0 ? houses[i] - heaters[index - 1] : Integer.MAX_VALUE;
                int right = index < heaters.length ? heaters[index] - houses[i] : Integer.MAX_VALUE;
                res = Math.max(Math.min(left, right), res);
            }
        }
        return res;
    }
}

这题真的不简单。。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页