Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1606. Find Servers That Handled Most Number of Requests - Leetcode Solution

Problem Description

Given k servers numbered from 0 to k-1, you are given two integer arrays: arrival and load, both of length n. The i-th request arrives at time arrival[i] and takes load[i] units of time to process.

Each incoming request is assigned to a server as follows:

  • Request i is assigned to server i % k if that server is available at arrival[i].
  • If not, try the next server (in increasing order, wrapping around to 0 if needed) until one is found or all servers have been checked.
  • If no server is free at arrival[i], the request is dropped.
After a server handles a request, it becomes unavailable until arrival[i] + load[i].

Your task is to return a list of servers that handled the most number of requests.

Constraints: Each request must be handled by at most one server. Servers cannot process more than one request at a time. Elements must not be reused.

Thought Process

The brute-force approach is to check each server for every incoming request to see if it's available, but this quickly becomes inefficient as the number of servers and requests grows. Instead, we need an efficient way to keep track of which servers are available at any given time, and to assign requests according to the rules.

We realize that we need to:

  • Efficiently find the next available server for each request, starting from i % k.
  • Keep track of when each server will be free.
  • Quickly update the set of available servers as requests finish.
The main challenge is efficiently simulating the server assignment and availability process for potentially large inputs.

Solution Approach

To solve the problem efficiently, we use two main data structures:

  • Min-heap (priority queue): To track busy servers and when they become available. Each entry is a tuple of (end time, server index).
  • Sorted set (or balanced BST): To track currently available servers. This allows us to quickly find the next available server starting from a given index, wrapping around if needed.

  1. For each incoming request (at time arrival[i]):
    • First, process the heap to release any servers that have finished their previous jobs by the current arrival time, adding them back to the available set.
    • Find the server to assign the request. Start searching from i % k in the available set. If none is found, wrap around and check earlier indices.
    • If a server is available, assign the request, increment its handled request count, and add it to the busy heap with its new end time.
    • If no server is available, drop the request.
  2. After all requests are processed, count the number of requests handled by each server and return those with the highest count.

Why these data structures?

  • The min-heap lets us efficiently release servers as soon as they become free.
  • The sorted set (e.g., TreeSet in Java, SortedSet in Python via bisect) allows efficient lookup for the next available server in the required order.

Example Walkthrough

Let's consider k = 3, arrival = [1,2,3,4,5], load = [5,2,3,3,3].

  1. Request 0: Arrives at 1, wants server 0. All servers are free. Assign to 0, which becomes busy until 6.
  2. Request 1: Arrives at 2, wants server 1. Server 1 is free. Assign to 1, busy until 4.
  3. Request 2: Arrives at 3, wants server 2. Server 2 is free. Assign to 2, busy until 6.
  4. Request 3: Arrives at 4, wants server 0. Server 0 is still busy (free at 6), but server 1 is now done (free at 4). Assign to 1, busy until 7.
  5. Request 4: Arrives at 5, wants server 1. Server 1 is busy (free at 7), server 2 is busy (free at 6), server 0 is busy (free at 6). No server is free, so drop the request.

Final counts: server 0: 1, server 1: 2, server 2: 1. Server 1 handled the most requests.

Time and Space Complexity

Brute-force: For each request, checking all servers is O(nk), which is too slow for large inputs.

Optimized approach:

  • Each request processes at most k servers in the worst case, but using a sorted set and heap, each operation is O(log k).
  • For n requests, total time is O(n \log k).
  • Space is O(k + n) for the heap, set, and request counts.

Summary

The problem tests your ability to efficiently simulate a resource allocation process with constraints. By using a min-heap to track busy servers and a sorted set to quickly find available servers, we can assign requests in optimal time. The key insight is to use data structures that allow for fast updates and lookups, making the algorithm both efficient and elegant.

Code Implementation

import heapq
import bisect

class Solution:
    def busiestServers(self, k, arrival, load):
        n = len(arrival)
        # Available servers (sorted list)
        available = list(range(k))
        # Busy servers: (end_time, server_index)
        busy = []
        # Count handled requests
        count = [0] * k

        for i, (start, duration) in enumerate(zip(arrival, load)):
            # Free up servers whose jobs are done
            while busy and busy[0][0] <= start:
                end_time, server = heapq.heappop(busy)
                bisect.insort(available, server)
            if not available:
                continue
            idx = bisect.bisect_left(available, i % k)
            if idx == len(available):
                idx = 0
            server = available.pop(idx)
            count[server] += 1
            heapq.heappush(busy, (start + duration, server))
        max_count = max(count)
        return [i for i, c in enumerate(count) if c == max_count]
      
#include <vector>
#include <set>
#include <queue>
using namespace std;

class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        int n = arrival.size();
        set<int> available;
        for (int i = 0; i < k; ++i) available.insert(i);
        // busy: pair<end_time, server_index>
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> busy;
        vector<int> count(k, 0);

        for (int i = 0; i < n; ++i) {
            int time = arrival[i];
            int duration = load[i];
            // Free up servers
            while (!busy.empty() && busy.top().first <= time) {
                available.insert(busy.top().second);
                busy.pop();
            }
            if (available.empty()) continue;
            auto it = available.lower_bound(i % k);
            if (it == available.end()) it = available.begin();
            int server = *it;
            available.erase(it);
            count[server]++;
            busy.emplace(time + duration, server);
        }
        int max_count = *max_element(count.begin(), count.end());
        vector<int> res;
        for (int i = 0; i < k; ++i)
            if (count[i] == max_count)
                res.push_back(i);
        return res;
    }
};
      
import java.util.*;

class Solution {
    public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
        int n = arrival.length;
        TreeSet<Integer> available = new TreeSet<>();
        for (int i = 0; i < k; ++i) available.add(i);
        PriorityQueue<int[]> busy = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        int[] count = new int[k];

        for (int i = 0; i < n; ++i) {
            int time = arrival[i], duration = load[i];
            while (!busy.isEmpty() && busy.peek()[0] <= time) {
                available.add(busy.poll()[1]);
            }
            Integer server = available.ceiling(i % k);
            if (server == null) server = available.isEmpty() ? null : available.first();
            if (server == null) continue;
            available.remove(server);
            count[server]++;
            busy.offer(new int[]{time + duration, server});
        }
        int max = Arrays.stream(count).max().getAsInt();
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < k; ++i)
            if (count[i] == max)
                res.add(i);
        return res;
    }
}
      
class MinHeap {
    constructor() { this.heap = []; }
    push(val) { this.heap.push(val); this._up(); }
    pop() { if (!this.heap.length) return null; const top = this.heap[0]; const last = this.heap.pop(); if (this.heap.length) { this.heap[0] = last; this._down(); } return top; }
    peek() { return this.heap[0]; }
    _up() { let i = this.heap.length - 1; while (i > 0) { let p = (i - 1) >> 1; if (this.heap[i][0] < this.heap[p][0]) [this.heap[i], this.heap[p]] = [this.heap[p], this.heap[i]], i = p; else break; } }
    _down() { let i = 0, n = this.heap.length; while (2 * i + 1 < n) { let l = 2 * i + 1, r = 2 * i + 2, j = l; if (r < n && this.heap[r][0] < this.heap[l][0]) j = r; if (this.heap[j][0] < this.heap[i][0]) [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]], i = j; else break; } }
}

var busiestServers = function(k, arrival, load) {
    let n = arrival.length;
    let available = [];
    for (let i = 0; i < k; ++i) available.push(i);
    let busy = new MinHeap();
    let count = new Array(k).fill(0);

    for (let i = 0; i < n; ++i) {
        let time = arrival[i], duration = load[i];
        // Free up servers
        while (busy.heap.length && busy.peek()[0] <= time) {
            let [_, server] = busy.pop();
            let idx = binaryInsert(available, server);
        }
        if (!available.length) continue;
        let idx = binarySearch(available, i % k);
        if (idx == available.length) idx = 0;
        let server = available.splice(idx, 1)[0];
        count[server]++;
        busy.push([time + duration, server]);
    }
    let max = Math.max(...count);
    let res = [];
    for (let i = 0; i < k; ++i)
        if (count[i] == max)
            res.push(i);
    return res;

    // insert server into available in sorted order
    function binaryInsert(arr, val) {
        let l = 0, r = arr.length;
        while (l < r) {
            let m = (l + r) >> 1;
            if (arr[m] < val) l = m + 1;
            else r = m;
        }
        arr.splice(l, 0, val);
        return l;
    }
    // find first available server >= val
    function binarySearch(arr, val) {
        let l = 0, r = arr.length;
        while (l < r) {
            let m = (l + r) >> 1;
            if (arr[m] < val) l = m + 1;
            else r = m;
        }
        return l;
    }
};