技术-算法-leetcode-1606. 找到处理最多请求的服务器(易懂版)

你需要解决的是服务器请求分配问题:给定k个环形排列的服务器和按时间递增到达的请求,按指定规则分配请求后,找出处理请求数量最多的所有服务器编号。这个问题的核心是高效管理服务器的空闲/忙碌状态,因为数据量(k和请求数)可达10^5,暴力解法会超时。

解题思路

要高效解决这个问题,需要两个核心数据结构:

  1. 有序集合(TreeSet):维护当前空闲的服务器编号,支持快速查找"i%k之后第一个空闲的服务器"(环形查找)。
  2. 最小堆(PriorityQueue):维护当前忙碌的服务器,存储(服务器结束时间,服务器编号),支持快速取出"最早结束的服务器",以便及时将其放回空闲集合。

具体步骤:

  1. 初始化空闲服务器集合(包含0~k-1)、忙碌服务器堆、请求计数数组。
  2. 遍历每个请求:
    • 先清理忙碌堆:将所有结束时间≤当前请求到达时间的服务器放回空闲集合。
    • 如果无空闲服务器,舍弃该请求。
    • 否则,按规则找到目标服务器:
      • 计算目标起点 target = i % k
      • 在空闲集合中找≥target的第一个服务器;若不存在,取空闲集合中最小的服务器(环形特性)。
    • 更新服务器状态:从空闲集合移除目标服务器,加入忙碌堆,计数数组+1。
  3. 遍历计数数组,找到最大值,收集所有等于最大值的服务器编号。

完整Java代码

java 复制代码
import java.util.*;

public class BusiestServers {
    public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
        // 1. 初始化数据结构
        // 空闲服务器集合:有序,支持快速查找/删除
        TreeSet<Integer> freeServers = new TreeSet<>();
        for (int i = 0; i < k; i++) {
            freeServers.add(i);
        }
        
        // 忙碌服务器堆:最小堆,元素为[结束时间, 服务器编号],按结束时间升序
        PriorityQueue<int[]> busyServers = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        
        // 记录每个服务器处理的请求数
        int[] count = new int[k];
        
        // 2. 处理每个请求
        int n = arrival.length;
        for (int i = 0; i < n; i++) {
            int arriveTime = arrival[i];
            int processTime = load[i];
            int endTime = arriveTime + processTime;
            
            // 步骤1:清理忙碌堆,将已完成的服务器放回空闲集合
            while (!busyServers.isEmpty() && busyServers.peek()[0] <= arriveTime) {
                int freeServer = busyServers.poll()[1];
                freeServers.add(freeServer);
            }
            
            // 步骤2:无空闲服务器,舍弃请求
            if (freeServers.isEmpty()) {
                continue;
            }
            
            // 步骤3:按规则找目标服务器
            int target = i % k;
            Integer selectedServer = freeServers.ceiling(target); // 找≥target的第一个空闲服务器
            if (selectedServer == null) {
                selectedServer = freeServers.first(); // 环形,取最小的空闲服务器
            }
            
            // 步骤4:更新服务器状态和计数
            freeServers.remove(selectedServer);
            busyServers.offer(new int[]{endTime, selectedServer});
            count[selectedServer]++;
        }
        
        // 3. 找出处理请求最多的服务器
        List<Integer> result = new ArrayList<>();
        int maxCount = 0;
        for (int c : count) {
            maxCount = Math.max(maxCount, c);
        }
        for (int i = 0; i < k; i++) {
            if (count[i] == maxCount) {
                result.add(i);
            }
        }
        
        return result;
    }

    // 测试用例
    public static void main(String[] args) {
        BusiestServers solution = new BusiestServers();
        // 示例1
        int k1 = 3;
        int[] arrival1 = {1,2,3,4,5};
        int[] load1 = {5,2,3,3,3};
        System.out.println(solution.busiestServers(k1, arrival1, load1)); // 输出[1]
        
        // 示例2
        int k2 = 3;
        int[] arrival2 = {1,2,3,4};
        int[] load2 = {1,2,1,2};
        System.out.println(solution.busiestServers(k2, arrival2, load2)); // 输出[0]
    }
}

代码关键部分解释

  1. TreeSet的核心操作
    • ceiling(target):返回≥target的最小元素,对应"找i%k之后第一个空闲服务器"。
    • first():返回集合中最小元素,对应环形查找时"从0开始找第一个空闲服务器"。
  2. PriorityQueue的核心作用
    • 按结束时间升序排列,确保每次能优先取出最早完成的服务器,及时释放资源。
  3. 计数数组
    • 简单直观地记录每个服务器处理的请求数,最后遍历即可找到最大值和对应服务器。

复杂度分析

  • 时间复杂度:O(n log k)。每个请求的堆操作(poll/offer)和集合操作(add/remove/ceiling)都是O(log k),n个请求总复杂度为O(n log k),满足10^5数据量的要求。
  • 空间复杂度:O(k)。空闲集合、忙碌堆、计数数组的空间均不超过k。

总结

  1. 核心数据结构:用TreeSet高效管理空闲服务器(支持环形查找),用PriorityQueue高效管理忙碌服务器(按结束时间排序)。
  2. 关键逻辑:处理每个请求前先清理已完成的服务器,再按环形规则查找目标空闲服务器。
  3. 效率保障:所有核心操作都是O(log k)级别,整体算法能高效处理10^5规模的输入。
相关推荐
looking_for__3 小时前
【Linux】应用层自定义协议与序列化
linux·服务器·网络
仰泳的熊猫3 小时前
题目1453:蓝桥杯历届试题-翻硬币
数据结构·c++·算法·蓝桥杯
啊阿狸不会拉杆3 小时前
《机器学习导论》第 10 章-线性判别式
人工智能·python·算法·机器学习·numpy·lda·线性判别式
会叫的恐龙3 小时前
C++ 核心知识点汇总(第11日)(排序算法)
c++·算法·排序算法
twilight_4693 小时前
机器学习与模式识别——线性回归算法
算法·机器学习·线性回归
晚风_END3 小时前
Linux|操作系统|elasticdump的二进制方式部署
运维·服务器·开发语言·数据库·jenkins·数据库开发·数据库架构
Lsir10110_3 小时前
【Linux】深入解剖页表——分页式存储
linux·运维·服务器
victory04313 小时前
服务器病毒处理记录
运维·服务器·chrome
玄同7653 小时前
Python Random 模块深度解析:从基础 API 到 AI / 大模型工程化实践
人工智能·笔记·python·学习·算法·语言模型·llm