{x}
blog image

Find Servers That Handled Most Number of Requests

You have k servers numbered from 0 to k-1 that are being used to handle multiple requests simultaneously. Each server has infinite computational capacity but cannot handle more than one request at a time. The requests are assigned to servers according to a specific algorithm:

  • The ith (0-indexed) request arrives.
  • If all servers are busy, the request is dropped (not handled at all).
  • If the (i % k)th server is available, assign the request to that server.
  • Otherwise, assign the request to the next available server (wrapping around the list of servers and starting from 0 if necessary). For example, if the ith server is busy, try to assign the request to the (i+1)th server, then the (i+2)th server, and so on.

You are given a strictly increasing array arrival of positive integers, where arrival[i] represents the arrival time of the ith request, and another array load, where load[i] represents the load of the ith request (the time it takes to complete). Your goal is to find the busiest server(s). A server is considered busiest if it handled the most number of requests successfully among all the servers.

Return a list containing the IDs (0-indexed) of the busiest server(s). You may return the IDs in any order.

 

Example 1:

Input: k = 3, arrival = [1,2,3,4,5], load = [5,2,3,3,3] 
Output: [1] 
Explanation: 
All of the servers start out available.
The first 3 requests are handled by the first 3 servers in order.
Request 3 comes in. Server 0 is busy, so it's assigned to the next available server, which is 1.
Request 4 comes in. It cannot be handled since all servers are busy, so it is dropped.
Servers 0 and 2 handled one request each, while server 1 handled two requests. Hence server 1 is the busiest server.

Example 2:

Input: k = 3, arrival = [1,2,3,4], load = [1,2,1,2]
Output: [0]
Explanation: 
The first 3 requests are handled by first 3 servers.
Request 3 comes in. It is handled by server 0 since the server is available.
Server 0 handled two requests, while servers 1 and 2 handled one request each. Hence server 0 is the busiest server.

Example 3:

Input: k = 3, arrival = [1,2,3], load = [10,12,11]
Output: [0,1,2]
Explanation: Each server handles a single request, so they are all considered the busiest.

 

Constraints:

  • 1 <= k <= 105
  • 1 <= arrival.length, load.length <= 105
  • arrival.length == load.length
  • 1 <= arrival[i], load[i] <= 109
  • arrival is strictly increasing.

Solution Explanation

This problem involves assigning incoming requests to servers, considering server availability and request load. The goal is to identify the servers that handled the most requests. The solution uses a combination of data structures to efficiently manage server availability and request processing.

Approach:

  1. Data Structures:

    • free: A sorted set (or equivalent) to store the IDs of available servers. A sorted set provides efficient insertion, deletion, and searching for the next available server.
    • busy: A min-heap (priority queue) to store the currently busy servers. Each element in the heap contains the server's ID and its release time. The min-heap ensures we can quickly identify the next available server.
    • cnt: An array to count the number of requests handled by each server.
  2. Request Processing:

    • Iterate through each request (arrival time, load).
    • Check for Available Servers: Remove any servers from the busy heap that have finished processing their requests (release time <= current arrival time). Add these servers back to the free set.
    • Assign Request:
      • If there are available servers: Assign the request to the next available server using the following approach:
        • Ideally, we'd assign it to i % k, but if that server is busy, we use the sorted set free to find the next available server in a wrapped-around manner (starting from i % k).
      • If no servers are available, drop the request (this is handled automatically since free will be empty).
    • Update States: Add the currently assigned server to the busy heap with its release time, remove the server from free, and increment its request count in cnt.
  3. Find Busiest Servers: After processing all requests, find the maximum number of requests handled among all servers (mx). Then, return the list of server IDs that have handled mx requests.

Time Complexity:

The time complexity is dominated by the iteration through the requests. Each operation on the free sorted set and the busy min-heap (insertion, deletion, searching) takes O(log k) time, where k is the number of servers. Therefore, the overall time complexity is O(N log k), where N is the number of requests.

Space Complexity:

The space complexity is O(k) to store free, busy, and cnt. This is because the number of servers is fixed and independent of the number of requests.

Code Explanation (Python):

The Python code utilizes the SortedList from the sortedcontainers library (a more efficient sorted set implementation than Python's built-in set). The code follows the algorithm described above:

from sortedcontainers import SortedList
import heapq
 
class Solution:
    def busiestServers(self, k: int, arrival: List[int], load: List[int]) -> List[int]:
        free = SortedList(range(k))
        busy = [] # Use a list as a min-heap
        cnt = [0] * k
        for i, (start, t) in enumerate(zip(arrival, load)):
            while busy and busy[0][0] <= start:
                free.add(busy[0][1])
                heapq.heappop(busy) # Correct heappop usage
            if not free:
                continue
            j = free.bisect_left(i % k)
            if j == len(free):
                j = 0
            server = free[j]
            cnt[server] += 1
            heapq.heappush(busy, (start + t, server)) # Correct heappush usage
            free.remove(server)
 
        mx = max(cnt)
        return [i for i, v in enumerate(cnt) if v == mx]

The other languages (Java, C++, Go) implement similar logic, using their respective priority queue and sorted set implementations. Note that Go's solution uses a custom heap and redblacktree for efficient priority queue and sorted set functionality. Java uses the standard PriorityQueue and TreeSet. C++ leverages priority_queue and set.