Heap / Priority Queue · Medium
LeetCode 2462: Total Cost to Hire K Workers — solution
Solve LeetCode 2462 with two min-heaps over the first and last candidates workers and two pointers that refill them — O((k + c) log c) time, O(c) space.
Problem in plain words
Workers stand in a line, each with a hiring cost. You run k hiring rounds. In each round you only look at the first candidates workers and the last candidates workers still in line (if those groups overlap, it is just everyone left). You hire the cheapest one among them; on a tie, the one with the smaller index wins. The hired worker leaves the line. Return the total cost of all k hires.
Example: costs = [6, 3, 8, 1, 5, 2], k = 3, candidates = 2. Round 1 sees 6, 3 and 5, 2 → hire 2. Round 2 sees 6, 3 and 1, 5 → hire 1. Round 3 sees 6, 3 and 8, 5 → hire 3. Total 6.
Intuition
Each round asks for the minimum of two small windows, and after a hire the window that lost a worker gains the next worker from the middle. Two min-heaps model the windows: left holds the front candidates and right the back ones. Pointers i and j mark the next unseen worker from each end, and i <= j guarantees nobody is placed in both heaps. On a tie between the two heap tops, pick left, since every front index is smaller than every back index.
Approach
- Set
i = 0,j = n - 1, and create two empty min-heaps. - At the start of each round, top up
leftfromi(moving right) andrightfromj(moving left) until each holdscandidatesworkers ori > j. - Compare the tops. If
rightis non-empty and (leftis empty orright's top is strictly smaller), hire fromright; otherwise hire fromleft. - Add the cost to the total and repeat for
krounds.
Dry run
costs = [6, 3, 8, 1, 5, 2], k = 3, candidates = 2.
| Round | left | right | Hire | Total |
|---|---|---|---|---|
| 1 | {3, 6} | {2, 5} | 2 (right) | 2 |
| 2 | {3, 6} | {1, 5} | 1 (right) | 3 |
| 3 | {3, 6} | {5, 8} | 3 (left) | 6 |
Java solution
class Solution {
public long totalCost(int[] costs, int k, int candidates) {
PriorityQueue<Integer> left = new PriorityQueue<>();
PriorityQueue<Integer> right = new PriorityQueue<>();
int i = 0;
int j = costs.length - 1;
long total = 0;
for (int round = 0; round < k; round++) {
while (left.size() < candidates && i <= j) {
left.offer(costs[i++]);
}
while (right.size() < candidates && i <= j) {
right.offer(costs[j--]);
}
if (!right.isEmpty() && (left.isEmpty() || right.peek() < left.peek())) {
total += right.poll();
} else {
total += left.poll();
}
}
return total;
}
}Python solution
class Solution:
def totalCost(self, costs: List[int], k: int, candidates: int) -> int:
left, right = [], []
i, j = 0, len(costs) - 1
total = 0
for _ in range(k):
while len(left) < candidates and i <= j:
heapq.heappush(left, costs[i])
i += 1
while len(right) < candidates and i <= j:
heapq.heappush(right, costs[j])
j -= 1
if right and (not left or right[0] < left[0]):
total += heapq.heappop(right)
else:
total += heapq.heappop(left)
return totalComplexity
| Measure | Cost | Why |
|---|---|---|
| Time | O((k + c) log c) | Up to 2c initial pushes, then each round does one pop and at most one refill push on heaps of size ≤ c. |
| Space | O(c) | The two heaps together hold at most 2 · candidates workers. |
Edge cases and common mistakes
- Overlapping windows — when
2 · candidates >= n, thei <= jcheck stops the second heap from re-adding workers already in the first. - Tie-breaking — on equal costs, the front worker has the smaller index, so take from
left(use strict<forright). - One heap runs empty — late in the process all remaining workers may sit in one heap; guard the comparison against empty heaps.
- Overflow — k · max cost can reach 10^10, so return a
longin Java.
›Can one heap do the job?
Yes: store (cost, side) or (cost, index) pairs in a single heap and refill from the side that was hired. The two-heap version keeps the tie-breaking and refill logic easier to read.
