Design Task Manager

medium design hash table heap ordered set

Problem

Implement a TaskManager seeded with [userId, taskId, priority] triples. Support add(userId, taskId, priority), edit(taskId, newPriority), rmv(taskId), and execTop(). execTop() runs the task with the highest priority; ties are broken by the highest taskId. It removes that task and returns its userId, or -1 if the system is empty.

Inputinit [[1,101,10],[2,102,20],[3,103,15]]; add(4,104,5); edit(102,8); execTop()
Output3
After the edit, priorities are 101→10, 102→8, 103→15, 104→5. The highest is task 103 (priority 15), so execTop returns its user, 3.

class TaskManager:
    def __init__(self, tasks):
        self.task = {}                 # taskId -> (userId, priority)
        self.heap = []                 # max-heap of (-priority, -taskId, taskId)
        for userId, taskId, priority in tasks:
            self.add(userId, taskId, priority)

    def add(self, userId, taskId, priority):
        self.task[taskId] = (userId, priority)
        heapq.heappush(self.heap, (-priority, -taskId, taskId))

    def edit(self, taskId, newPriority):
        userId, _ = self.task[taskId]
        self.task[taskId] = (userId, newPriority)
        heapq.heappush(self.heap, (-newPriority, -taskId, taskId))

    def rmv(self, taskId):
        del self.task[taskId]          # lazy: stale heap entry skipped later

    def execTop(self):
        while self.heap:
            negp, _, taskId = self.heap[0]
            cur = self.task.get(taskId)
            if cur is not None and cur[1] == -negp:   # entry still current
                heapq.heappop(self.heap)
                del self.task[taskId]
                return cur[0]
            heapq.heappop(self.heap)   # drop stale entry
        return -1
class TaskManager {
  constructor(tasks) {
    this.task = new Map();             // taskId -> [userId, priority]
    this.heap = new MaxHeap();         // ordered by (priority, taskId)
    for (const [userId, taskId, priority] of tasks)
      this.add(userId, taskId, priority);
  }
  add(userId, taskId, priority) {
    this.task.set(taskId, [userId, priority]);
    this.heap.push([priority, taskId]);
  }
  edit(taskId, newPriority) {
    const [userId] = this.task.get(taskId);
    this.task.set(taskId, [userId, newPriority]);
    this.heap.push([newPriority, taskId]);
  }
  rmv(taskId) {
    this.task.delete(taskId);          // lazy: stale heap entry skipped later
  }
  execTop() {
    while (this.heap.size() > 0) {
      const [priority, taskId] = this.heap.peek();
      const cur = this.task.get(taskId);
      if (cur && cur[1] === priority) { // entry still current
        this.heap.pop();
        this.task.delete(taskId);
        return cur[0];
      }
      this.heap.pop();                 // drop stale entry
    }
    return -1;
  }
}
class TaskManager {
    private Map<Integer, int[]> task = new HashMap<>();  // taskId -> {userId, priority}
    // max-heap by priority, then taskId
    private PriorityQueue<int[]> heap = new PriorityQueue<>(
        (a, b) -> a[0] != b[0] ? b[0] - a[0] : b[1] - a[1]);  // {priority, taskId}

    public TaskManager(List<List<Integer>> tasks) {
        for (List<Integer> t : tasks) add(t.get(0), t.get(1), t.get(2));
    }
    public void add(int userId, int taskId, int priority) {
        task.put(taskId, new int[]{userId, priority});
        heap.offer(new int[]{priority, taskId});
    }
    public void edit(int taskId, int newPriority) {
        int userId = task.get(taskId)[0];
        task.put(taskId, new int[]{userId, newPriority});
        heap.offer(new int[]{newPriority, taskId});
    }
    public void rmv(int taskId) {
        task.remove(taskId);            // lazy: stale heap entry skipped later
    }
    public int execTop() {
        while (!heap.isEmpty()) {
            int[] top = heap.peek();
            int[] cur = task.get(top[1]);
            if (cur != null && cur[1] == top[0]) {  // entry still current
                heap.poll();
                task.remove(top[1]);
                return cur[0];
            }
            heap.poll();                // drop stale entry
        }
        return -1;
    }
}
class TaskManager {
    unordered_map<int, pair<int,int>> task;   // taskId -> {userId, priority}
    // max-heap of {priority, taskId}: bigger priority, then bigger taskId, wins
    priority_queue<pair<int,int>> heap;
public:
    TaskManager(vector<vector<int>>& tasks) {
        for (auto& t : tasks) add(t[0], t[1], t[2]);
    }
    void add(int userId, int taskId, int priority) {
        task[taskId] = {userId, priority};
        heap.push({priority, taskId});
    }
    void edit(int taskId, int newPriority) {
        int userId = task[taskId].first;
        task[taskId] = {userId, newPriority};
        heap.push({newPriority, taskId});
    }
    void rmv(int taskId) {
        task.erase(taskId);             // lazy: stale heap entry skipped later
    }
    int execTop() {
        while (!heap.empty()) {
            auto [priority, taskId] = heap.top();
            auto it = task.find(taskId);
            if (it != task.end() && it->second.second == priority) { // current
                heap.pop();
                int userId = it->second.first;
                task.erase(it);
                return userId;
            }
            heap.pop();                 // drop stale entry
        }
        return -1;
    }
};
Time: O(log n) amortized per operation Space: O(n + total pushes)