06
Heap-assisted greedy (Greedy Sub-pattern)
The best next choice depends on what you did so far, so keep the candidates in a heap (or an ordered map) and pop the current best each time.
Signals in the problem statement
- ›
"no two adjacent characters are equal"
- ›
"split into groups of consecutive cards"
- ›
"cooldown between equal tasks"
- ›
"merge the two smallest", "minimise max − min"
What makes it different
Most frequent first (Reorganize String) → max-heap by count. Smallest card first (Hand of Straights) → ordered map. Two smallest (Huffman) → min-heap. Lower the maximum (Minimize Deviation) → max-heap, track the min.
Questions
ordered from “learn the idea” to “recognise it without help”- 1Reorganize StringWalkthroughMediumBasic Anchor·LeetCode #767
- 2Hand of StraightsWalkthroughMediumBasic Reinforcement·LeetCode #846
- 3Task SchedulerWalkthroughMediumSlight Variation·LeetCode #621
- 4Huffman CodingWalkthroughMediumMedium·GeeksforGeeks
- 5Minimize Deviation in ArrayWalkthroughHardPattern Transfer·LeetCode #1675
Template
heap-greedy-template.cpp
// Repeatedly merge the two smallest (Huffman cost, connecting ropes).
long long mergeTwoSmallest(const vector<int>& w) {
priority_queue<long long, vector<long long>, greater<long long>> pq(w.begin(), w.end());
long long cost = 0;
while (pq.size() > 1) {
long long a = pq.top(); pq.pop();
long long b = pq.top(); pq.pop();
cost += a + b;
pq.push(a + b); // the merged node may be the next smallest
}
return cost;
}
// Most frequent first, never the same as the last one (no two equal neighbours).
string noTwoAdjacent(const string& s) {
int cnt[26] = {};
for (char ch : s) cnt[ch - 'a']++;
priority_queue<pair<int,char>> pq; // {count, letter}, biggest count on top
for (int c = 0; c < 26; c++) if (cnt[c]) pq.push({cnt[c], char('a' + c)});
string out;
while (pq.size() >= 2) {
auto [c1, x] = pq.top(); pq.pop();
auto [c2, y] = pq.top(); pq.pop();
out += x; out += y; // two different letters per round
if (--c1) pq.push({c1, x});
if (--c2) pq.push({c2, y});
}
if (!pq.empty()) {
if (pq.top().first > 1) return ""; // one letter left over more than once
out += pq.top().second;
}
return out;
}Remember this · Greedy
Trigger
Maximise count / minimise cost with a natural "best next step", sorting makes the choice obvious, reachability.
Think
Is there a choice I can always make safely without looking ahead?
Key question
Why can't the greedy choice be worse than the optimal one? (Try to break it with a small example.)