力扣刷题日记-面试题 1488 湖泊洪水问题

72 阅读4分钟
  • js版本写了个小顶堆来实现最近要抽水的湖泊,但是放到力扣中抛弃了超时了,不知道怎么优化了,但是java版本是不超时的,真奇怪,两个版本的思路是一样的.
  • 分别搞了三个容器来装,map容器是将同一个湖泊下雨的时间收集到一起.小顶堆heap用来划分需要抽水的湖泊的优先级,根据时间排序,最近要下雨,肯定最先抽干哪个湖泊,不然就洪水了.set用来存现在哪些湖泊是满水的,如果再次下雨下到满水的湖泊,那就直接洪水了.

js版本:

// leetcode 1488 湖泊问题js版本
// 先准备一个小顶堆用来处理干活(抽干哪个湖泊的优先级)的优先级
class MinHeap {
  constructor() {
    this.heap = []
  }
  getParentIndex(i) {
    return (i - 1) >> 1
  }
  getLeftIndex(i) {
    return (i * 2) + 1
  }
  getRightIndex(i) {
    return (i * 2) + 2
  }
  shiftDown(index) {
    const leftIndex = this.getLeftIndex(index)
    const rightIndex = this.getRightIndex(index)
    if(this.heap[leftIndex] && this.heap[leftIndex].nextRain < this.heap[index].nextRain) {
      this.swap(leftIndex,index)
      this.shiftDown(leftIndex)
    }
    if(this.heap[rightIndex] && this.heap[rightIndex].nextRain < this.heap[index].nextRain) {
      this.swap(rightIndex,index)
      this.shiftDown(rightIndex)
    }
  }
  shiftUp(index) {
    if(index == 0) return
    const parentIndex = this.getParentIndex(index)
    if(this.heap[parentIndex].nextRain > this.heap[index].nextRain){
      this.swap(parentIndex,index)
      this.shiftUp(parentIndex)
    } 
  }
  swap(i1,i2) {
    const temp = this.heap[i1]
    this.heap[i1] = this.heap[i2]
    this.heap[i2] = temp
  }
  insert(value) {
    this.heap.push(value)
    this.shiftUp(this.heap.length - 1)
  }
  // 删除堆顶的元素
  pop() {
    const t = this.heap[0] 
    if(this.size() == 1) {
      this.heap = []
      return t
    }
    
    this.heap[0] = this.heap.pop()
    this.shiftDown(0)
    return t
  }
  // 获取堆顶
  peek() {
    return this.heap[0]
  }
  size() {
    return this.heap.length
  }
}

var avoidFlood = function(rains) {
  let n = rains.length
  let ans = [] // 没有洪水的返回数组,数组中有值
  let invalid = [] // 洪水来了,直接返回空数组


  let map = new Map() // 用来记录某个湖泊在哪些天会下雨
  for(let i = 0; i < n; i++) {
    if(rains[i] != 0) {
      // 第i天有湖泊下雨
      if(!map.get(rains[i])) {
        map.set(rains[i],[])
      }
      map.get(rains[i]).push(i)
    }
  }

  // 没抽干的湖泊表
  // 某个湖泊如果满了,加入到set里
  // 某个湖泊被抽干了,从set中剔除
  let set = new Set()
  // 用小顶堆来分配最先处理哪个湖泊,也就是最先需要干的活
  const heap = new MinHeap()
  for(let i = 0; i < n; i++) {
    console.log('rains',i,rains[i])
    console.log('截止上次待抽干的湖泊set',set)
    console.log('截止上次待干的活heap',heap.heap)
    if(rains[i] != 0) { // 今天下雨,不干活返回ans[ℹ] = -1,但是要准备干活的计划,
      if(set.has(rains[i])) {
        console.log('rains==>2',i,rains[i])
        console.log('set==>',set)
        // 该湖泊原本没抽干,这次又下雨,必然洪水,直接返回空数组
        return invalid
      }
      // 放入到没抽干的列表
      set.add(rains[i])
      // 同时该湖泊在哪些天下雨,最前面的时间可以去除掉了
      map.get(rains[i]).shift()
      //
      if(map.get(rains[i]).length != 0 ) {
        // 说明该湖泊后面会下雨,可能会有洪水,我得找个合适的时间事先抽干,不然会发洪水
        // 所以添加任务到小顶堆
        heap.insert({lake: rains[i],nextRain: map.get(rains[i])[0]})
      }
      ans[i] = -1 // 
    }else {
      // 今天干活,抽干某个湖泊的水,肯定是先抽干最近要下雨的湖泊,躲避洪水呀
      // 看看小顶堆的干活表中有没有活可以干
      if(heap.size() == 0) {
        // 没有活可干
        ans[i] = 1  // 题目规定,无活可干返回1
      }else {
        let work = heap.pop()
        console.log('work',work)
        console.log('workset',set)
        set.delete(work.lake)
        ans[i] = work.lake
        console.log('从待抽干的set抽干',set)
        console.log('从待干的活中干完一个',heap)
      }
    }
    console.log('----------------------------------')
  }
  return ans
};

let ans = avoidFlood([1,0,1,0,2,0,2])//[-1,1,-1,1,-1,2,-1]
console.log(ans)

java版本:



package class52;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.PriorityQueue;

public class Problem_1488_AvoidFloodInTheCity {

	// 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()) {
					ans[i] = 1;// 规定: 没活可干写死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;
		}
	}

}
//[1,0,1,0,2,0,2]     [-1,1,-1,1,-1,2,-1]