Talentd
FleetCode
Make and follow sheets.Sign in

Process Tasks Using Servers solution and explanation

MediumArrayHeap (Priority Queue)8 min read42.0% acceptanceUpdated 9 Apr 2026
Process Tasks Using Servers - Leetcode 1882 - PythonNeetCode · 17:24 · 19K viewsWatch on YouTube
5 more videos on this problem
  1. Process Tasks Using Servers - Leetcode 1882 - PythonNeetCode · 17:24 · 19K views
  2. LeetCode 1882. Process Tasks Using ServersHappy Coding · 16:55 · 1K views
  3. 【每日一题】1882. Process Tasks Using Servers, 6/6/2021Huifeng Guan · 30:09 · 839 views
  4. LeetCode Weekly Contest 243 Question 3: Process Tasks Using ServersCoding Ninjas Webinars and Contest Editorials · 22:26 · 715 views
  5. 1882. Process Tasks Using Servers (Leetcode Medium)Programming Live with Larry · 44:15 · 628 views
  6. LeetCode 1882: Process Tasks Using ServersAlitaCode · 31:06 · 18 views

The problem

You are given two 0-indexed integer arrays servers and tasks of lengths n​​​​​​ and m​​​​​​ respectively. servers[i] is the weight of the i​​​​​​th​​​​ server, and tasks[j] is the time needed to process the j​​​​​​th​​​​ task in seconds.

Tasks are assigned to the servers using a task queue. Initially, all servers are free, and the queue is empty.

At second j, the jth task is inserted into the queue (starting with the 0th task being inserted at second 0). As long as there are free servers and the queue is not empty, the task in the front of the queue will be assigned to a free server with the smallest weight, and in case of a tie, it is assigned to a free server with the smallest index.

If there are no free servers and the queue is not empty, we wait until a server becomes free and immediately assign the next task. If multiple servers become free at the same time, then multiple tasks from the queue will be assigned in order of insertion following the weight and index priorities above.

A server that is assigned task j at second t will be free again at second t + tasks[j].

Build an array ans​​​​ of length m, where ans[j] is the index of the server the j​​​​​​th task will be assigned to.

Return the array ans​​​​.

Example 1:

Input: servers = [3,3,2], tasks = [1,2,3,2,1,2]
Output: [2,2,0,2,1,2]
Explanation: Events in chronological order go as follows:
- At second 0, task 0 is added and processed using server 2 until second 1.
- At second 1, server 2 becomes free. Task 1 is added and processed using server 2 until second 3.
- At second 2, task 2 is added and processed using server 0 until second 5.
- At second 3, server 2 becomes free. Task 3 is added and processed using server 2 until second 5.
- At second 4, task 4 is added and processed using server 1 until second 5.
- At second 5, all servers become free. Task 5 is added and processed using server 2 until second 7.

Example 2:

Input: servers = [5,1,4,3,2], tasks = [2,1,2,4,5,2,1]
Output: [1,4,1,4,1,3,2]
Explanation: Events in chronological order go as follows: 
- At second 0, task 0 is added and processed using server 1 until second 2.
- At second 1, task 1 is added and processed using server 4 until second 2.
- At second 2, servers 1 and 4 become free. Task 2 is added and processed using server 1 until second 4. 
- At second 3, task 3 is added and processed using server 4 until second 7.
- At second 4, server 1 becomes free. Task 4 is added and processed using server 1 until second 9. 
- At second 5, task 5 is added and processed using server 3 until second 7.
- At second 6, task 6 is added and processed using server 2 until second 7.

Constraints:

  • servers.length == n
  • tasks.length == m
  • 1 <= n, m <= 2 * 105
  • 1 <= servers[i], tasks[j] <= 2 * 105

Approach overview

Problem Overview: You are given multiple servers with different weights and a list of tasks arriving each second. Each task must be assigned to the available server with the smallest weight (and smallest index if tied). If no server is free, the system waits until the next server becomes available and processes the queued tasks.

Approach 1: Simulate Time and Check Availability (O(n * m) time, O(n) space)

This method directly simulates the system timeline. At each task arrival time, iterate through all servers and pick the free one with the smallest weight and index. If all servers are busy, advance the current time to when the earliest server finishes and retry the assignment. You maintain server states (busy until time) in a simple structure and repeatedly scan to find the best candidate. The approach uses basic array operations and explicit time simulation.

The implementation is straightforward but inefficient for large inputs because each task assignment may require scanning all servers. With n servers and m tasks, worst-case time becomes O(n * m). Space complexity stays O(n) for tracking server availability.

Approach 2: Priority Queue for Servers (O((n + m) log n) time, O(n) space)

This approach uses two heap (priority queue) structures to manage server states efficiently. One min-heap stores available servers ordered by (weight, index). Another min-heap tracks busy servers ordered by their next available time. As each task arrives, first move any servers whose completion time is less than or equal to the current time from the busy heap back to the available heap.

If an available server exists, pop the best one and assign the task. If none are available, fast-forward time to the earliest finishing server, release it, and continue scheduling. Each assignment and state transition uses heap push/pop operations, keeping the process efficient. This avoids scanning all servers and ensures that selecting the optimal server always takes O(log n) time.

Recommended for interviews: The priority queue solution is the expected answer. It demonstrates strong understanding of scheduling systems, heaps, and event simulation. Starting with the brute-force simulation helps show reasoning, but interviewers typically look for the optimized heap-based scheduler with O((n + m) log n) complexity.

Approach 1: Priority Queue for Servers

In this approach, use two priority queues: one to manage the free servers based on weight and index, and another to track busy servers (when they will be free). At each timestamp, assign available servers to tasks in the queue.

from heapq import heappop, heappush

def assign_tasks(servers, tasks):
    n = len(servers)
    m = len(tasks)
    free_servers = [(servers[i], i) for i in range(n)]
    busy_servers = []  # (free_time, weight, index)
    result = [-1] * m
    queue = []  # task indices
    current_time = 0

    for time in range(m):
        while busy_servers and busy_servers[0][0] <= time:
            free_time, weight, server_index = heappop(busy_servers)
            heappush(free_servers, (weight, server_index))

        queue.append(time)

        while free_servers and queue:
            task_index = queue.pop(0)
            weight, server_index = heappop(free_servers)
            result[task_index] = server_index
            heappush(busy_servers, (time + tasks[task_index], weight, server_index))

        current_time += 1

    return result
import java.util.*;

public class TaskAssigner {
    public int[] assignTasks(int[] servers, int[] tasks) {
        int n = servers.length;
        int m = tasks.length;
        PriorityQueue<int[]> freeServers = new PriorityQueue<>((a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]));
        PriorityQueue<int[]> busyServers = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        int[] result = new int[m];

        for (int i = 0; i < n; i++) {
            freeServers.offer(new int[]{servers[i], i});
        }

        Queue<Integer> taskQueue = new ArrayDeque<>();
        int currentTime = 0;

        for (int time = 0; time < m; time++) {
            while (!busyServers.isEmpty() && busyServers.peek()[0] <= time) {
                int[] server = busyServers.poll();
                freeServers.offer(new int[]{server[1], server[2]});
            }

            taskQueue.offer(time);

            while (!freeServers.isEmpty() && !taskQueue.isEmpty()) {
                int taskIdx = taskQueue.poll();
                int[] server = freeServers.poll();
                result[taskIdx] = server[1];
                busyServers.offer(new int[]{time + tasks[taskIdx], server[0], server[1]});
            }

            currentTime++;
        }

        return result;
    }
}

The solution uses two priority queues to manage available and busy servers effectively. Free servers are sorted by weight and index, while busy servers are tracked by the time they will become free.

Complexity

Time Complexity: O((n + m) log n), where n is the number of servers and m is the number of tasks, due to the heap operations.
Space Complexity: O(n + m) to store the priority queues and the result array.

Try this approach in the editor

Approach 2: Simulate Time and Check Availability

Simulate the process by checking each time point for available servers. If a server becomes free, handle tasks in the order of their arrival and manage availability accordingly.

#include <vector>
#include <queue>
using namespace std;

vector<int> assignTasks(vector<int>& servers, vector<int>& tasks) {
    int n = servers.size();
    int m = tasks.size();
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> free_servers;
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> busy_servers;
    vector<int> result(m);
    queue<int> q;
    int time = 0;

    for (int i = 0; i < n; ++i) {
        free_servers.emplace(servers[i], i);
    }

    for (int j = 0; j < m; ++j) {
        while (!busy_servers.empty() && get<0>(busy_servers.top()) <= time) {
            auto [free_time, weight, idx] = busy_servers.top();
            busy_servers.pop();
            free_servers.emplace(weight, idx);
        }

        q.push(j);

        while (!free_servers.empty() && !q.empty()) {
            int task_idx = q.front(); q.pop();
            auto [weight, server_idx] = free_servers.top(); free_servers.pop();
            result[task_idx] = server_idx;
            busy_servers.emplace(time + tasks[task_idx], weight, server_idx);
        }

        time++;
    }

    return result;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
    int weight;
    int index;
} Server;

typedef struct {
    int time;
    int weight;
    int index;
} BusyServer;

int cmpServers(const void* a, const void* b) {
    Server* serverA = (Server*)a;
    Server* serverB = (Server*)b;
    if (serverA->weight == serverB->weight) {
        return serverA->index - serverB->index;
    }
    return serverA->weight - serverB->weight;
}

int cmpBusyServers(const void* a, const void* b) {
    BusyServer* busyA = (BusyServer*)a;
    BusyServer* busyB = (BusyServer*)b;
    return busyA->time - busyB->time;
}

int* assignTasks(int* servers, int serversSize, int* tasks, int tasksSize, int* returnSize) {
    Server* freeServers = malloc(sizeof(Server) * serversSize);
    BusyServer* busyServers = malloc(sizeof(BusyServer) * tasksSize);
    int* res = malloc(sizeof(int) * tasksSize);
    int currentTime = 0;
    int busyCount = 0;
    int taskQueue[tasksSize];
    int queueStart = 0, queueEnd = 0;

    for (int i = 0; i < serversSize; i++) {
        freeServers[i] = (Server){servers[i], i};
    }

    qsort(freeServers, serversSize, sizeof(Server), cmpServers);

    for (int time = 0; time < tasksSize; time++) {
        while (busyCount > 0 && busyServers[0].time <= time) { // Free servers
            Server server = {busyServers[0].weight, busyServers[0].index};
            memmove(busyServers, busyServers + 1, (--busyCount) * sizeof(BusyServer));
            int insertPos = serversSize - busyCount - 1;
            memmove(freeServers + insertPos + 1, freeServers + insertPos, busyCount * sizeof(Server));
            freeServers[insertPos] = server;
            qsort(freeServers, serversSize, sizeof(Server), cmpServers);
        }

        taskQueue[queueEnd++] = time;

        while (queueStart < queueEnd && serversSize - busyCount > 0) {
            int taskIndex = taskQueue[queueStart++];
            Server server = freeServers[serversSize - busyCount - 1];
            busyServers[busyCount++] = (BusyServer){time + tasks[taskIndex], server.weight, server.index};
            server.index = -1; // Mark as used
            qsort(busyServers, busyCount, sizeof(BusyServer), cmpBusyServers);
            res[taskIndex] = server.index;
        }

        currentTime++;
    }

    *returnSize = tasksSize;
    free(freeServers);
    free(busyServers);
    return res;
}

This C++ solution simulates task assignment over time using two priority queues for free and busy servers, ensuring tasks are processed in arrival order via a separate queue.

Complexity

Time Complexity: O((n + m) log n), owing to the priority queue handling of free and busy servers.
Space Complexity: O(n + m), accounting for server management and result tracking.

Try this approach in the editor

Approach 3: Priority Queue (Min-Heap)

We use a min-heap idle to maintain all idle servers, where each element is a tuple (x, i) representing the i-th server with weight x. We use another min-heap busy to maintain all busy servers, where each element is a tuple (w, s, i) representing the i-th server that will be idle at time w with weight s. Initially, we add all servers to idle.

Next, we iterate through all tasks. For the j-th task, we first remove all servers from busy that will be idle at or before time j and add them to idle. Then we take the server with the smallest weight from idle, add it to busy, and assign it to the j-th task. If idle is empty, we take the server with the earliest idle time from busy, add it to busy, and assign it to the j-th task.

After iterating through all tasks, we obtain the answer array ans.

The time complexity is O((n + m) log n), where n is the number of servers and m is the number of tasks. The space complexity is O(n). Here, n and m are the number of servers and tasks, respectively.

Similar problems:

function assignTasks(servers: number[], tasks: number[]): number[] {
    const idle = new PriorityQueue({
        compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]),
    });
    const busy = new PriorityQueue({
        compare: (a, b) =>
            a[0] === b[0] ? (a[1] === b[1] ? a[2] - b[2] : a[1] - b[1]) : a[0] - b[0],
    });
    for (let i = 0; i < servers.length; ++i) {
        idle.enqueue([servers[i], i]);
    }
    const ans: number[] = [];
    for (let j = 0; j < tasks.length; ++j) {
        const t = tasks[j];
        while (busy.size() > 0 && busy.front()![0] <= j) {
            const [_, s, i] = busy.dequeue()!;
            idle.enqueue([s, i]);
        }
        if (idle.size() > 0) {
            const [s, i] = idle.dequeue()!;
            busy.enqueue([j + t, s, i]);
            ans.push(i);
        } else {
            const [w, s, i] = busy.dequeue()!;
            busy.enqueue([w + t, s, i]);
            ans.push(i);
        }
    }
    return ans;
}
func assignTasks(servers []int, tasks []int) (ans []int) {
	idle := hp{}
	busy := hp2{}
	for i, x := range servers {
		heap.Push(&idle, pair{x, i})
	}
	for j, t := range tasks {
		for len(busy) > 0 && busy[0].w <= j {
			p := heap.Pop(&busy).(tuple)
			heap.Push(&idle, pair{p.s, p.i})
		}
		if idle.Len() > 0 {
			p := heap.Pop(&idle).(pair)
			ans = append(ans, p.i)
			heap.Push(&busy, tuple{j + t, p.s, p.i})
		} else {
			p := heap.Pop(&busy).(tuple)
			ans = append(ans, p.i)
			heap.Push(&busy, tuple{p.w + t, p.s, p.i})
		}
	}
	return
}

type pair struct {
	s int
	i int
}

type hp []pair

func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool {
	a, b := h[i], h[j]
	return a.s < b.s || a.s == b.s && a.i < b.i
}
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v any)   { *h = append(*h, v.(pair)) }
func (h *hp) Pop() any     { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }

type tuple struct {
	w int
	s int
	i int
}

type hp2 []tuple

func (h hp2) Len() int { return len(h) }
func (h hp2) Less(i, j int) bool {
	a, b := h[i], h[j]
	return a.w < b.w || a.w == b.w && (a.s < b.s || a.s == b.s && a.i < b.i)
}
func (h hp2) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp2) Push(v any)   { *h = append(*h, v.(tuple)) }
func (h *hp2) Pop() any     { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }
Try this approach in the editor

Complexity comparison

ApproachComplexity
Priority Queue for Servers

Time Complexity: O((n + m) log n), where n is the number of servers and m is the number of tasks, due to the heap operations.
Space Complexity: O(n + m) to store the priority queues and the result array.

Simulate Time and Check Availability

Time Complexity: O((n + m) log n), owing to the priority queue handling of free and busy servers.
Space Complexity: O(n + m), accounting for server management and result tracking.

Priority Queue (Min-Heap)–

Complexity analysis

ApproachTimeSpaceWhen to Use
Simulate Time and Check AvailabilityO(n * m)O(n)Useful for understanding the scheduling process or when constraints are small
Priority Queue for ServersO((n + m) log n)O(n)Best general solution for large inputs and expected approach in interviews

Frequently asked questions

Is Process Tasks Using Servers easy or hard?

Process Tasks Using Servers is classified as a Medium problem on LeetCode. The difficulty comes from coordinating two priority queues and correctly simulating time when all servers are busy.

Process Tasks Using Servers Python/Java solution

Python solutions typically use the heapq module to maintain available and busy server heaps. Java implementations use PriorityQueue with custom comparators for ordering by weight, index, and completion time. Both implementations achieve O((n + m) log n) complexity.

What is the best approach for Process Tasks Using Servers?

The most efficient approach uses two priority queues (heaps). One heap tracks available servers ordered by weight and index, while another tracks busy servers by their completion time. This structure allows efficient scheduling and server release operations, giving a total time complexity of O((n + m) log n).

How to solve Process Tasks Using Servers in O((n+m) log n)?

Maintain two min-heaps: one for available servers sorted by (weight, index) and one for busy servers sorted by next available time. For each task arrival, release completed servers, assign the best available server, and push it into the busy heap with its finishing time. Heap operations keep scheduling efficient.

Is Process Tasks Using Servers asked at Google/Amazon/Meta?

Variants of server scheduling and task assignment problems appear frequently in interviews at companies like Amazon, Google, and Meta. The question tests knowledge of priority queues, event simulation, and system-style scheduling logic.

What data structure is used in Process Tasks Using Servers?

The core data structure is a min-heap (priority queue). One heap prioritizes available servers by weight and index, while another heap manages busy servers by their next available time. Arrays are also used to store the final assignment results.

What is the time complexity of Process Tasks Using Servers?

The optimal heap-based solution runs in O((n + m) log n) time, where n is the number of servers and m is the number of tasks. Each task assignment and server state update involves heap push or pop operations that take O(log n). Space complexity is O(n).

Comments

Now write it yourselfClose this page and solve Process Tasks Using Servers in the editor, without looking.
Practise Process Tasks Using Servers