题目

类型:贪心
image.png

解题思路

开一个静态数组 cnts 来充当哈希表,同时维护一个当前处理的最大任务数量 max,最终所有满足
cnst[i]=max的机台集合即是答案

根据「每个任务有对应的开始时间和持续时间」以及「任务分配规则」,使用优先队列(堆)和有序集合(红黑树)来进行维护。

利用「每个任务有对应的开始时间和持续时间」,使用优先队列(堆)维护二元组 (idx,endTime)。其中 idx 为机器编号, endTime 为当前机台所处理任务的结束时间(也就是该机台最早能够接受新任务的时刻)

对于每个 arrival[i] 而言(新任务),先从优先队列中取出所有 endTime⩽arrival[i] 的机台 idx,加入「空闲池」,然后再按照「任务分配规则」从空闲池子中取机台,若取不到,则丢弃该任务。

由于「任务分配规则」是优先取大于等于 i % k 的最小值,若取不到,再取大于等于 0 的最小值。
因此「空闲池」最好是支持「二分」的有序集合,容易想到基于「红黑树」的 TreeSet 结构。

代码

  1. class Solution {
  2. public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
  3. //统计处理任务数
  4. int[] cnts = new int[k];
  5. Arrays.fill(cnts, 0);
  6. int n = arrival.length, max = 0;
  7. // Map<Integer, Integer> map = new HashMap<>();
  8. //优先队列存储当前忙碌的服务器索引及其任务结束时间,根据结束时间升序排列
  9. PriorityQueue<int[]> busy = new PriorityQueue<>((a,b)->a[1]-b[1]);
  10. //红黑树集合 存储当前任务开始时刻,所有空闲的服务器索引
  11. TreeSet<Integer> free = new TreeSet<>();
  12. //初始所有服务器均空闲,全部加入到free
  13. for (int i = 0; i < k; i++) free.add(i);
  14. //依次遍历每一个任务
  15. for (int i = 0; i < n; i++) {
  16. //当前待分配任务的开始时刻与结束时刻
  17. int start = arrival[i], end = start + load[i];
  18. //找出所有当前忙碌的服务器的任务结束时刻早于待分配任务的开始时刻的,都加入到free集合中等待筛选
  19. while (!busy.isEmpty() && busy.peek()[1] <= start) free.add(busy.poll()[0]);
  20. //用u来表示当前轮次待分配到的服务器索引,优先取 >= i%k的
  21. Integer u = free.ceiling(i % k);
  22. //若取不到,则取>=0的
  23. if (u == null) u = free.ceiling(0);
  24. //若依然取不到,则放弃该任务
  25. if (u == null) continue;
  26. //取到了,将该服务器索引从free中移除
  27. free.remove(u);
  28. //与此同时,将该服务器加入到忙碌队列中,存储一个长度为2的数组,[0]:索引 [1]:任务结束时刻
  29. busy.add(new int[]{u, end});
  30. //更新当前最大的处理任务数目
  31. max = Math.max(max, ++cnts[u]);
  32. }
  33. //将最忙碌的服务器索引筛选出来加入List
  34. List<Integer> ans = new ArrayList<>();
  35. for (int i = 0; i < k; i++) {
  36. if (cnts[i] == max) ans.add(i);
  37. }
  38. return ans;
  39. }
  40. }