Python custom max priority queue
IntuitionWe need a dictionary that maps each task to the priority and user, and a max heap that orders the tasks based on priority and task_id.Approach
Initiali
# Intuition
We need a dictionary that maps each task to the priority and user, and a max heap that orders the tasks based on priority and task_id.

# Approach
1. Initialize a dict mapping task_id to its corresponding priority and user_id.
2. Create a custom Node class that has the metadata (user_id, task_id, and priority) with an overridden `__lt__` method based on priority and task_id. We will use this Node class for our max heap.
3. Initialize a max heap pushing Node objects with the input parameters mentioned in step 2.
4. For add, edit methods, we will update the dictionary based on the task_id key and pushing a new node onto the max heap.
5. For rmv method, just delete the entry from the dictionary based on the task_id.
6. For execTop, we need to first check if the dictionary is empty (which means we return -1). Then we pop the top off the heap as long as the top item was already changed or removed through edit/rmv methods. The top task after this little while loop should be the top task we are looking for. Be sure to also delete this entry from dictionary before we return the user_id from this top task item.

# Complexity
- Time complexity:
 $$O(n*log(n))$$ for `__init__`, `add`, `edit`, `execTop` methods, 
$$O(1)$$ for `rmv` method
- Space complexity:
$$O(n)$$ 

# Code
```python3 []
from heapq import heappop, heappush
class Node:
    def __init__(self, user_id, task_id, priority):
        self.user_id = user_id
        self.task_id = task_id
        self.priority = priority

    def __lt__(self, other):
        if self.priority == other.priority:
            return self.task_id > other.task_id
        return self.priority > other.priority

class TaskManager:

    def __init__(self, tasks: List[List[int]]):
        self.task_dict = {}
        self.pq = []
        for task in tasks:
            user_id = task[0]
            task_id = task[1]
            priority = task[2]
            self.task_dict[task_id] = [priority, user_id]
            heappush(self.pq, Node(user_id, task_id, priority))

    def add(self, userId: int, taskId: int, priority: int) -> None:
        self.task_dict[taskId] = [priority, userId]
        heappush(self.pq, Node(userId, taskId, priority))

    def edit(self, taskId: int, newPriority: int) -> None:
        task = self.task_dict[taskId]
        userId = task[1]
        self.task_dict[taskId]= [newPriority, userId]
        heappush(self.pq, Node(userId, taskId, newPriority))

    def rmv(self, taskId: int) -> None:
        del self.task_dict[taskId]

    def execTop(self) -> int:
        if len(self.task_dict) == 0:
            return -1
        while self.pq[0].task_id not in self.task_dict or self.pq[0].priority != self.task_dict[self.pq[0].task_id][0] or self.pq[0].user_id != self.task_dict[self.pq[0].task_id][1]:
            heappop(self.pq)

        top_task = heappop(self.pq)
        top_task_id = top_task.task_id
        del self.task_dict[top_task_id]
        top_task_user = top_task.user_id
        return top_task_user


# Your TaskManager object will be instantiated and called as such:
# obj = TaskManager(tasks)
# obj.add(userId,taskId,priority)
# obj.edit(taskId,newPriority)
# obj.rmv(taskId)
# param_4 = obj.execTop()


# Your TaskManager object will be instantiated and called as such:
# obj = TaskManager(tasks)
# obj.add(userId,taskId,priority)
# obj.edit(taskId,newPriority)
# obj.rmv(taskId)
# param_4 = obj.execTop()
```
['Python3']