不好意思,读完题目第一时间想到了Frostpunk。
不错的建造管理游戏,就是最后的立意不禁让人反思白皮的奇葩想法。
这群人需要党的光辉照耀。
1、先读题
说回正题,先来看题目吧
读到一半时,还以为又是道关于图的题。结果却是只有一维的数组题目,难度顿时降了大半。毕竟还只是中等嘛
2、审题
题目倒是没有什么陷阱,只是存在一些没有指明的信息,从而有些误导。
来看重点吧:
- 房屋和加热器都处于统一直线,因而加热半径相当于计算直线上的两点距离即可。
- 要求得出最小的加热半径,即为所有房屋和最近的加热器的距离中的最大值。
- 房屋和加热器数组的顺序不定。
是的,重中之重实际上是第3点。
题目中并没有明确的说明,但三个示例case全都是严格递增的,从而埋下了一些伏笔。
3、思路
Winter is coming~
其实题目的要求很简单,我们也很好进行问题的拆分。
就如同审题中第2点所指出的,题目询问的最小加热半径,即是所有房屋与最近的加热器的距离中的最大值。
于是,我们就可以拆分为,先查找每个房屋与最近的加热器的距离,然后再比较之间的最大值即可。
辣么,如何确定房屋与最近的加热器的距离呢?
我们首先使用两个指针,一个指向houses[]
,一个指向heaters[]
。在遍历house[]
的同时,也让指向heaters[]
的指针逐步滑动。
如何查找与房屋最近的加热器呢?
我们在得出房屋与当前加热器的距离后,再让一个临时指针指向加热器指针的下一位,并逐步滑动该临时指针,直到临时指针指向的加热器的距离大于加热器指针指向的加热器的距离。
此时,移动加热器指针到该临时指针上。
你可能会有疑问:就这样移动加热器指针吗?难道之后的房屋,不会在之前的加热器中得到最优解吗?
我们不妨考虑下,假如houses[i]
距离最近的加热器为heaters[j]
,当我们考察houses[i+1]
时,由于houses[i+1] > houses[i]
,且由于heaters[j]
为houses[i]
的最优解,则有Dis(i,j) <= Dis(i, j-1)
。 辣么Dis(i+1,j) <= Dis(i+1, j-1)
,即houses[i+1]
的最优解,必在heaters[j]
或之后产生。
前提是:houses[]
和heaters[]
严格递增。
这是我忽略了的前提,导致提交了后,才从错误的case中发现了端倪,改正了代码。
4、开工
class Solution {
public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int minRadius = -1;
int house = 0, heater = 0;
while (house < houses.length) {
//当前的距离
int dis = getDis(heaters[heater], houses[house]);
int nextHeater = Math.min(heater + 1, heaters.length);
while (nextHeater < heaters.length) {
//下一个加热器与当前房屋的距离
int nextDis = getDis(heaters[nextHeater], houses[house]);
//大于时,继续移动也无法得出更优解
if (nextDis > dis) {
break;
}
//将加热器的指针指向下一加热器
heater = nextHeater;
//小于时,说明更优解可能存在于后续加热器中,继续移动下一加热器指针
nextHeater++;
dis = nextDis;
}
//最小半径,为每一房屋的最优解中的最大值
minRadius = Math.max(minRadius, dis);
//移到下一房屋
++house;
}
return minRadius;
}
private int getDis(int house, int heater) {
return Math.abs(house - heater);
}
}
发现问题后,匆匆忙忙在开头补了两个排序
5、解读
getDis()
方法用来计算房屋与加热器的实际距离,不用多说,重点来看主函数。
基本上与思路中所讲的一致,两个指针,分别指向房屋和加热器数组,同时使用一个临时的加热器数组指针,向下移动得出每个房屋的最优解。
注意,题目标签所说的双指针,并不是指我这里的房屋指针和加热器指针,而是加热器数组上的指针(
heater
)和临时指针(nextHeater
)。其实这里完全没必要为房屋数组添加指针。
最后,每次移动房屋指针时,比较最小半径和当前的最优解,得出其中的最大值,即为题目要求的最小半径。
6、提交
耗时排名让我有些惊讶。
以我的脑袋是想不出任何优化的空间了,不知道是有什么步骤浪费了些时间,还是有更牛逼的解法。
7、咀嚼
假设,houses[]
和heaters[]
的长度分别为N和M。
辣么我们对两个数组都进行了一遍排序,耗时为O(MlogM + NlogN),之后又各进行了一次遍历,耗时为O(M+N)。
故整体为O(MlogM + NlogN);
空间复杂度O(1)。
这么想来耗时几乎全在排序上了,可没有这层排序,我的思路就完全没法展开。
看看有没有大牛支招吧。
8、他人之糟糠,我之精魄
遗憾,又没有什么高科技。
我对比了大牛们贴出的大部分题解,我这套思路又与最优解不谋而合,重点在于我在某些步骤浪费了些许时间,才没有做出100%耗时排名。
官解提供了两种解法,除了我们这套外,还有基于二分查找的,不过也很好想,遗憾的是时间复杂度逊于双指针的解法。
这里还是放出官解,有大牛在评论中贴出了java 100%耗时排名的解法。
9、总结
就到这吧。
今天的题没有什么收获,权当巩固下双指针的使用吧。
各位社畜周一愉快。o(╥﹏╥)o
最后题外话,还是安利下FrostPunk这款游戏吧。
而且马上也要出2代了。