1606. 找到处理最多请求的服务器 : 数据结构运用题

简介: 1606. 找到处理最多请求的服务器 : 数据结构运用题

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 1606. 找到处理最多请求的服务器 ,难度为 困难


Tag : 「数据结构」、「优先队列」、「堆」、「红黑树」、「二分」

你有 kk 个服务器,编号为 00k-1k1 ,它们可以同时处理多个请求组。


每个服务器有无穷的计算能力但是不能同时处理超过一个请求。


请求分配到服务器的规则如下:


  • ii(序号从 00 开始)个请求到达。
  • 如果所有服务器都已被占据,那么该请求被舍弃(完全不处理)。
  • 如果第 ( i % k ) 个服务器空闲,那么对应服务器会处理该请求。
  • 否则,将请求安排给下一个空闲的服务器(服务器构成一个环,必要的话可能从第 00 个服务器开始继续找下一个空闲的服务器)。比方说,如果第 ii 个服务器在忙,那么会查看第 ( i+1i+1 ) 个服务器,第 ( i+2i+2 ) 个服务器等等。
  • 给你一个严格递增的正整数数组 arrival,表示第 ii 个任务的到达时间,和另一个数组 load ,其中 load[i]load[i] 表示第 ii 个请求的工作量(也就是服务器完成它所需要的时间)。你的任务是找到 最繁忙的服务器 。最繁忙定义为一个服务器处理的请求数是所有服务器里最多的。


请你返回包含所有最繁忙服务器序号的列表,你可以以任意顺序返回这个列表。


示例 1:


网络异常,图片无法展示
|


输入:k = 3, arrival = [1,2,3,4,5], load = [5,2,3,3,3] 
输出:[1] 
解释:
所有服务器一开始都是空闲的。
前 3 个请求分别由前 3 台服务器依次处理。
请求 3 进来的时候,服务器 0 被占据,所以它呗安排到下一台空闲的服务器,也就是服务器 1 。
请求 4 进来的时候,由于所有服务器都被占据,该请求被舍弃。
服务器 0 和 2 分别都处理了一个请求,服务器 1 处理了两个请求。所以服务器 1 是最忙的服务器。
复制代码


示例 2:


输入:k = 3, arrival = [1,2,3,4], load = [1,2,1,2]
输出:[0]
解释:
前 3 个请求分别被前 3 个服务器处理。
请求 3 进来,由于服务器 0 空闲,它被服务器 0 处理。
服务器 0 处理了两个请求,服务器 1 和 2 分别处理了一个请求。所以服务器 0 是最忙的服务器。
复制代码


示例 3:


输入:k = 3, arrival = [1,2,3], load = [10,12,11]
输出:[0,1,2]
解释:每个服务器分别处理了一个请求,所以它们都是最忙的服务器。
复制代码


示例 4:


输入:k = 3, arrival = [1,2,3,4,8,9,10], load = [5,2,10,3,1,2,2]
输出:[1]
复制代码


示例 5:


输入:k = 1, arrival = [1], load = [1]
输出:[0]
复制代码


提示:


  • 1 <= k <= 10^51<=k<=105
  • 1 <= arrival.length, load.length <= 10^51<=arrival.length,load.length<=105
  • arrival.length == load.lengtharrival.length==load.length
  • 1 <= arrival[i], load[i] <= 10^91<=arrival[i],load[i]<=109
  • arrival 保证严格递增。


数据结构



题目要统计处理任务数最多的机器,首先容易想到使用「哈希表」统计每个机台处理的任务数,利用机台数量 kk 最多不超过 10^5105,我们可以开一个静态数组 cnts 来充当哈希表,同时维护一个当前处理的最大任务数量 max,最终所有满足 cnst[i] = \maxcnst[i]=max 的机台集合即是答案。


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


具体的,利用「每个任务有对应的开始时间和持续时间」,我们使用优先队列(堆)维护二元组 (idx, endTime)(idx,endTime),其中 idxidx 为机器编号,endTimeendTime 为当前机台所处理任务的结束时间(也就是该机台最早能够接受新任务的时刻),对于每个 arrival[i]arrival[i] 而言(新任务),我们先从优先队列中取出所有 endTime < arrival[i]endTime<arrival[i] 的机台 idxidx,加入「空闲池」,然后再按照「任务分配规则」从空闲池子中取机台,若取不到,则丢弃该任务。


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


代码:


class Solution {
    static int N = 100010;
    static int[] cnts = new int[N];
    public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
        Arrays.fill(cnts, 0);
        int n = arrival.length, max = 0;
        PriorityQueue<int[]> busy = new PriorityQueue<>((a,b)->a[1]-b[1]);
        TreeSet<Integer> free = new TreeSet<>();
        for (int i = 0; i < k; i++) free.add(i);
        for (int i = 0; i < n; i++) {
            int start = arrival[i], end = start + load[i];
            while (!busy.isEmpty() && busy.peek()[1] <= start) free.add(busy.poll()[0]);
            Integer u = free.ceiling(i % k);
            if (u == null) u = free.ceiling(0);
            if (u == null) continue;
            free.remove(u);
            busy.add(new int[]{u, end});
            max = Math.max(max, ++cnts[u]);
        }
        List<Integer> ans = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            if (cnts[i] == max) ans.add(i);
        }
        return ans;
    }
}
复制代码


  • 时间复杂度:令任务数量为 nn,机台数量为 kk,起始将所有机台存入 TreeSet,复杂度为 O(k\log{k})O(klogk);每次处理新的 arrival[i]arrival[i] 时,先从优先队列取出可接受新任务的机台,存入 TreeSet,然后从 TreeSet 中取出最多一个的机台来完成任务,其中从 TreeSet 中取出机台最多调用两次的 ceiling 操作,复杂度为 O(\log{k})O(logk),这部分的整体复杂度为 O(n\log{k})O(nlogk);统计处理任务数达到 max 的机台集合复杂度为 O(k)O(k);整体复杂度为 O((k + n)\log{k})O((k+n)logk)
  • 空间复杂度:O(k)O(k)


最后



这是我们「刷穿 LeetCode」系列文章的第 No.1606 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
4月前
|
Swift iOS开发
iOS Swift使用Alamofire请求本地服务器报错-1002
iOS Swift使用Alamofire请求本地服务器报错-1002
119 1
|
4月前
|
开发框架 缓存 .NET
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
229 0
|
2月前
|
JSON JavaScript 前端开发
《进阶篇第6章:vue中的ajax》包括回顾发送ajax请求方式、vue-cli脚手架配置代理服务器、vue-resource
《进阶篇第6章:vue中的ajax》包括回顾发送ajax请求方式、vue-cli脚手架配置代理服务器、vue-resource
61 22
|
2月前
|
前端开发 JavaScript Java
第6章:Vue中的ajax(包含:回顾发送ajax请求方式、vue-cli脚手架配置代理服务器)
第6章:Vue中的ajax(包含:回顾发送ajax请求方式、vue-cli脚手架配置代理服务器)
83 4
|
2月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
60 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
3月前
|
开发者
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
39 1
|
4月前
|
缓存 数据安全/隐私保护 UED
代理服务器在HTTP请求中的应用:Ruby实例
代理服务器在HTTP请求中的应用:Ruby实例
|
5月前
|
存储 运维 Java
函数计算产品使用问题之如何使用Python的requests库向HTTP服务器发送GET请求
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
108 8
|
5月前
|
Shell Python
`pytest-httpserver`是一个pytest插件,它允许你在测试期间启动一个轻量级的HTTP服务器,并模拟HTTP请求和响应。
`pytest-httpserver`是一个pytest插件,它允许你在测试期间启动一个轻量级的HTTP服务器,并模拟HTTP请求和响应。
|
5月前
|
前端开发 JavaScript
【node写接口】 通过node 快速搭建一个服务器、get请求、post请求 小白入门
【node写接口】 通过node 快速搭建一个服务器、get请求、post请求 小白入门
181 4