一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
一、题目
你的国家有无数个湖泊,所有湖泊一开始都是空的。当第 n 个湖泊下雨的时候,那么它就会装满水。如果第 n 个湖泊是 满的 ,这个湖泊会发生 洪水 。你的目标是避免任意一个湖泊发生洪水。
给你一个整数数组 rains ,其中:
- rains[i] > 0 表示第 i 天时,第 rains[i] 个湖泊会下雨。
- rains[i] == 0 表示第 i 天没有湖泊会下雨,你可以选择 一个 湖泊并 抽干 这个湖泊的水。
请返回一个数组 ans ,满足:
- ans.length == rains.length
- 如果 rains[i] > 0 ,那么ans[i] == -1 。
- 如果 rains[i] == 0 ,ans[i] 是你第 i 天选择抽干的湖泊。
如果有多种可行解,请返回它们中的 任意一个 。如果没办法阻止洪水,请返回一个 空的数组 。
二、分析
这题不看解释,读不懂题,翻译的啥玩意。。。其次还有坑的地方,没湖可抽,为啥返回1号湖,题目也没说清楚呀!!!
下雨天不干活,干活的时候不下雨。
下雨天没法干活,只有不下雨的时候选一个(选的时候有讲究)湖泊(i)把水抽干才能避免下次下雨导致湖泊(i)发生洪水,因为原来湖泊(i)有水不抽干,再次下雨就导致发生洪水了
准备一个表:存放有雨的湖泊号(没抽干的湖泊),当不下雨的时候,看右边哪个湖离的近,就抽那个湖
当来到不下雨的时候,怎么快速查找到比较近的湖泊呢?不通过遍历的方式
{1, 2, 0, 2, 3, 0, 1, 0, 3}
0 1 2 3 4 5 6 7 8
准备一张表(每个湖泊在哪天下雨):key:湖泊号, value:天(数组下标)列表
1:{0,6}, 2:{1,3}, 3:{4,8}
set(存放有雨水的湖泊,待抽水)
有序表或小根堆(干活表), 根据天(数组下标)从小到大排序
三、实现
// rains[i] = j 第i天轮到j号湖泊下雨
// 规定,下雨日,不干活 : -1
// 不下雨日,如果没有湖泊可抽 : 1
public static int[] avoidFlood(int[] rains) {
int n = rains.length;
int[] ans = new int[n];
int[] invalid = new int[0];
// key : 某个湖
// value : 这个湖在哪些位置降雨
// 4 : {3,7,19,21}
// 1 : { 13 }
// 2 : {4, 56}
HashMap<Integer, LinkedList<Integer>> map = new HashMap<>();
for (int i = 0; i < n; i++) {
if (rains[i] != 0) { // 第i天要下雨,rains[i]
// 3天 9号
// 9号 { 3 }
// 9号 {1, 3}
if (!map.containsKey(rains[i])) {
map.put(rains[i], new LinkedList<>());
}
map.get(rains[i]).addLast(i);
}
}
// 没抽干的湖泊表
// 某个湖如果满了,加入到set里
// 某个湖被抽干了,从set中移除
HashSet<Integer> set = new HashSet<>();
// 这个堆的堆顶表示最先处理的湖是哪个
PriorityQueue<Work> heap = new PriorityQueue<>();
for (int i = 0; i < n; i++) { // 0 1 2 3 ...
if (rains[i] != 0) { // 当前湖有水
if (set.contains(rains[i])) {
return invalid;
}
// 放入到没抽干的表里
set.add(rains[i]);
map.get(rains[i]).pollFirst();
if (!map.get(rains[i]).isEmpty()) {
heap.add(new Work(rains[i], map.get(rains[i]).peekFirst()));
}
// 题目规定
ans[i] = -1;
} else { // 今天干活!
if (heap.isEmpty()) { // 不下雨日,如果没有湖泊可抽 : 1
ans[i] = 1;
} else {
Work cur = heap.poll();
set.remove(cur.lake);
ans[i] = cur.lake;
}
}
}
return ans;
}
public static class Work implements Comparable<Work> {
public int lake;
public int nextRain;
public Work(int l, int p) {
lake = l;
nextRain = p;
}
@Override
public int compareTo(Work o) {
return nextRain - o.nextRain;
}
}
四、总结
整体时间复杂度为O(N),遍历一次搞定
堆的调整代价为O(logN)
如果通过遍历查找,则整体时间复杂度为O(N²)