This problem naturally suggests using a heap, but you need to implement a custom heap. Although C++'s priority_queue is also a heap, it does not provide methods to modify elements, which greatly limits its use. However, the following pure heap implementation passes 660/663 test cases, but test case 660 timed out (TLE).
voidshiftUp(size_t index) { while (index != 0) { if (!cmp(vec[parent(index)], vec[index])) { break; } // vec[index] returns a reference to the element, not a copy. swap(vec[parent(index)], vec[index]); index = parent(index); } } voidshiftDown(size_t index) { int n = vec.size(); while (true) { size_t left = left_child(index); size_t right = right_child(index); size_t largest = index;
if (left < n && cmp(vec[largest], vec[left])) largest = left; if (right < n && cmp(vec[largest], vec[right])) largest = right; if (largest == index) break; swap(vec[index], vec[largest]); index = largest; } }
voidadd(int userId, int taskId, int priority) { vector<int> vec; vec.push_back(userId); vec.push_back(taskId); vec.push_back(priority); // Copy max_heap.push(vec); }
voidedit(int taskId, int newPriority) { int index = -1; int oldPriority; for (int i = 0; i < max_heap.vec.size(); i++) { if (max_heap.vec[i][1] == taskId) { index = i; oldPriority = max_heap.vec[i][2]; max_heap.vec[i][2] = newPriority; break; } } if (index == -1) { return; }
voidrmv(int taskId) { int index = -1; int oldPriority; for (int i = 0; i < max_heap.vec.size(); i++) { if (max_heap.vec[i][1] == taskId) { index = i; oldPriority = max_heap.vec[i][2]; break; } } if (index == -1) { return; }
if (index == max_heap.vec.size() - 1) { max_heap.vec.pop_back(); return; }
We previously said that priority_queue cannot delete. Using the lazy deletion method, as long as the element that should be deleted is not the maximum, it is not deleted. After this problem, I understand why priority_queue does not provide methods to modify or delete non-top elements. Lazy deletion thumbs up 👍
Note that there are two types of lazy deletion in this problem: 1. Caused by modification, taskId may appear repeatedly in the heap, so it is not enough to only check whether taskId exists in the map; 2. Caused by deletion, taskId disappears;
voidadd(int userId, int taskId, int priority) { heap.emplace(priority, taskId); map[taskId] = { priority, userId }; }
voidedit(int taskId, int newPriority) { if (map.find(taskId) != map.end()) { map[taskId].first = newPriority; heap.emplace(newPriority, taskId); } }
voidrmv(int taskId) { map.erase(taskId); }
intexecTop() { int top_userId, top_taskId, top_priority;
while (!heap.empty()) { top_priority = heap.top().first; top_taskId = heap.top().second;
if (map.find(top_taskId) != map.end() && map[top_taskId].first == top_priority) { // Exists, and priority is the same top_userId = map[top_taskId].second; map.erase(top_taskId); heap.pop(); return top_userId; } else { // Element that should be deleted heap.pop(); } } return-1; } };
/** * Your TaskManager object will be instantiated and called as such: * TaskManager* obj = new TaskManager(tasks); * obj->add(userId,taskId,priority); * obj->edit(taskId,newPriority); * obj->rmv(taskId); * int param_4 = obj->execTop(); */
Among them, heap.emplace(…) is a member function of the C++ standard library priority_queue, used to directly construct elements in place in the heap, rather than creating an object first and then copying or moving it in. It is similar to push() in functionality, but is usually more efficient than push(), especially when constructing complex objects.
Syntax
1 2
priority_queue<T> heap; heap.emplace(args...);
args… are directly passed to the constructor of T Equivalent to:
1
heap.push(T(args...));
But emplace avoids the creation of temporary objects. Example