Leetcode 1606. Find Servers That Handled Most Number of Requests

Problem Explanation

In this problem, we 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 can handle only one request at a given time. The requests are assigned to servers according to a specific algorithm: if all servers are busy, the incoming request is dropped, and if a particular server is available, the incoming request is assigned to that server. Otherwise, the request is assigned to the next available server. We are then given an array 'arrival' representing the arrival time of the ith request, and another array 'load' representing the load of the ith request (time it takes to complete). The goal is to find the busiest server(s), where a server is considered busiest if it handled the most number of requests successfully. The task is to return a list containing the IDs (0-indexed) of the busiest server(s).

To solve this problem, we use the concept of priority queue along with the set data structure to keep track of the currently idle servers.

Walkthrough of an Example

Let's consider an example to understand the problem: k=3, arrival=[1,2,3,4,5], load=[5,2,3,3,3]

At time 1, request 0 comes in, so it's handled by server 0.

At time 2, request 1 comes in, and server 1 handles it.

At time 3, request 2 comes in, it’s handled by server 2.

At time 4, request 3 comes. Server 0 is busy, so the request is assigned to the next available server, which is server 1.

At time 5, request 4 comes, but all servers are busy, so request 4 is dropped.

Hence server 1 is the busiest server as it handled two requests.

Approach of the Solution

In the solution, a minHeap (priority queue) is used to store the time when each server becomes available and the corresponding server id. A set (idleServers) is used to store the id's of currently idle servers. We iterate over each request in the arrival array and assign it to the server as per the given conditions.

  1. If there are any idle servers, we try to assign the request to server with the same id of the request modulo k. If this server is not available, we try to find the next higher available server. If no servers are available, the request is dropped.
  2. If the server is found, we increase the server's request count and add a new entry to the heap denoting when this server will be free. We also remove this server from the set of idle servers.
  3. If the server is not found, we drop the request.
  4. Before considering each new request, we also check the heap for any servers that have become free before or at the time of arrival of the new request and add those servers in the set.

Finally, we return the servers with the maximum number of requests handled.

Python Answer

1
2python
3Class Solution:
4    def busiestServers(self, k: int, arrival: List[int], load: List[int]) -> List[int]:
5        avail = [i for i in range(k)]
6        servers = [0]*k
7        q = []
8        
9        for i in range(len(arrival)):
10            while q and q[0][0] <= arrival[i]:
11                _,idx = heapq.heappop(q)
12                bisect.insort(avail,idx)
13            if avail:
14                idx = bisect.bisect(avail,i%k)
15                if idx == len(avail): 
16                    idx = 0
17                server = avail.pop(idx)
18                servers[server] += 1
19                heapq.heappush(q, (arrival[i]+load[i], server))
20
21        max_req = max(servers)
22        return [i for i, x in enumerate(servers) if x == max_req]

Java Answer

1
2java
3class Solution {
4    public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
5        int n = arrival.length;
6        int[] servers = new int[k];
7        PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->a[0]-b[0]);
8        TreeSet<Integer> available = new TreeSet<>();
9        for(int i = 0; i < k; i++) {
10            available.add(i);
11        }
12        for(int i = 0; i < n; i++) {
13            while(!pq.isEmpty() && pq.peek()[0] <= arrival[i]) {
14                available.add(pq.poll()[1]);
15            }
16            if(available.isEmpty()) {
17                continue;
18            }
19            Integer next = available.ceiling(i%k);
20            if(next == null) {
21                next = available.first();
22            }
23            servers[next]++;
24            pq.add(new int[]{arrival[i] + load[i], next});
25            available.remove(next);
26        }
27        int max = 0;
28        for(int s:servers) {
29            max = Math.max(max, s);
30        }
31        List<Integer> ans = new ArrayList<>();
32        for(int i = 0; i < k; i++) {
33            if(servers[i] == max) {
34                ans.add(i);
35            }
36        }
37        return ans;
38    }
39}

JavaScript Answer

1
2javascript
3var busiestServers = function(k, arrival, load) {
4    const endTimes = new Array(k).fill(0);
5    let serverCounts = new Array(k).fill(0);
6    const curr = new Set();
7    for(let i = 0; i < k; i++) curr.add(i);
8    const pq = [];
9    for(let i = 0; i < arrival.length; i++) {
10        while(pq.length && arrival[i] >= pq[0][0]) {
11            curr.add(pq[0][1]);
12            pq.shift();
13        }
14        if(i < k) {
15            serverCounts[i]++;
16            pq.push([arrival[i]+load[i], i]);
17            pq.sort((a, b) => a[0] - b[0]);
18            curr.delete(i);
19            continue;
20        }
21        if(!curr.size) continue;
22        for(let j = i%k; ;j++) {
23            let server = j%k;
24            if(curr.has(server)) {
25                serverCounts[server]++;
26                pq.push([arrival[i]+load[i],server]);
27                pq.sort((a, b) => a[0] - b[0]);
28                curr.delete(server);
29                break;
30            }
31        }
32    }
33    const max = Math.max(...serverCounts);
34    const ans = [];
35    for(let i = 0; i < serverCounts.length; i++) {
36        if(serverCounts[i] === max) ans.push(i);
37    }
38    return ans;
39};

C# Answer

1
2csharp
3public class Solution {
4    public IList<int> BusiestServers(int k, int[] arrival, int[] load) {
5        var endTimes = new int[k];
6        var idleIdx = new SortedSet<int>(Enumerable.Range(0, k));
7        var counts = new int[k];
8        var tasks = new SortedSet<(int end, int server)>();
9
10        for (int i = 0; i < arrival.Length; i++) {
11            var task = (endl: arrival[i], serverl: 0);
12            while (tasks.Count > 0 && tasks.Min.end <= arrival[i]) {
13                task = tasks.Min;
14                tasks.Remove(task);
15                idleIdx.Add(task.server);
16            }
17  
18            if (idleIdx.Count == 0) continue;
19            var idleArray = idleIdx.ToArray();
20            var idx = Array.BinarySearch(idleArray, i % k);
21            idx = idx < 0 ? ~idx : idx;
22            idx %= idleIdx.Count;
23            var server = idleArray[idx];
24            counts[server]++;
25            idleIdx.Remove(server);
26            tasks.Add((arrival[i] + load[i], server));
27        }
28
29        var max = counts.Max();
30        var results = new List<int>();
31        for (var i = 0; i < counts.Length; i++) {
32            if (counts[i] == max) results.Add(i);
33        }
34
35        return results;        
36    }
37}

Conclusion

The problem is mainly about managing the servers' number of requests and then finding the busiest servers. The problem is solved using a priority queue to manage the server's load and a set to manage the idle servers.This problem encourages a thorough application of concepts such as priority queues, arrays, sets, and data structure. It requires an understanding of how to manage servers and requests in a systematic order to identify which can cater to the maximum number of requests.

Implementing the solution in the four programming languages (Python, Java, JavaScript and C#) has its minor differences due to their syntax and built-in functions. However, the approach to solving the problem remains the same across all platforms.

One can improve performance by taking steps such as avoiding unnecessary work such as recreating arrays by reducing redundancy, employing logic to drop requests when no servers are available, optimal use of data structures like priority queues and sets to manage server's load and their idle time.

Moreover, through understanding and solving this problem, developers can get more insights into the process of request handling in servers and load balancing, making it more applicable in real-world applications. This is not only a problem-solving task but also a good simulation of real-world computing scenarios.

In conclusion, being able to solve this kind of problem can prove to be very beneficial for persons seeking roles that require problem-solving skills in data management, algorithm development, distributed computing environments, and especially in domains requiring load balancing mechanisms like web development, cloud computing, and data centers.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫