Opening the reading…
Opening the reading…
HASHING › IMPLEMENTARY PROBLEMS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →The string is sorted by how often each character appears, so every copy of one character belongs together in the answer. First count the copies of each character. If a character appears 5 times, its entire group has priority over every character appearing 4 times, regardless of the characters' alphabetical order.
A character's frequency can be at most n, where n is the string length. That gives a direct index for every group: bucket[f] stores all characters appearing f times, already repeated f times. Reading the buckets from n down to 1 produces the required order without comparing characters against one another.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | Counting touches each of the n input characters once. Distributing characters writes exactly n output copies across the buckets, and scanning the n possible frequency indices visits each index once, so the total remains O(n). |
| Space | O(n) extra | The bucket array has n + 1 entries, and its stored character groups contain n copies in total; the result is required output and is excluded from the extra-space bound. The bound stays O(n) even when one character fills one bucket or every character occupies a different bucket. |
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;
class Solution {
public:
string frequencySort(string s) {
unordered_map<char, int> freq;
for (char c : s) {
++freq[c];
}
vector<string> bucket(s.size() + 1);
for (const auto& entry : freq) {
char c = entry.first;
int count = entry.second;
bucket[count].append(count, c);
}
string result;
for (int count = static_cast<int>(s.size()); count >= 1; --count) {
result += bucket[count];
}
return result;
}
};The append call is the key construction step: bucket[count].append(count, c) writes the whole group immediately, so the later scan only concatenates ready-made pieces. The loop starts at s.size() because that is the largest possible frequency, then moves downward. No tie-breaking is needed because the statement accepts any order among characters with equal frequency.
A max-heap can store one entry for each distinct character, ordered by frequency. Repeatedly remove the largest entry and append its character count times. This is a different arrangement, not an optimisation for this problem: it uses O(n log k) time and O(k) heap space, where k is the number of distinct characters, while the bucket method uses O(n) time and O(n) extra space.
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;
class Solution {
public:
string frequencySort(string s) {
unordered_map<char, int> freq;
for (char c : s) {
++freq[c];
}
priority_queue<pair<int, char>> heap;
for (const auto& entry : freq) {
heap.push({entry.second, entry.first});
}
string result;
while (!heap.empty()) {
auto [count, c] = heap.top();
heap.pop();
result.append(count, c);
}
return result;
}
};The heap version is useful when frequencies are not naturally bounded by a manageable integer range, or when you want to process only the next highest-frequency group at a time. Here the frequency range is exactly 1 through n, so buckets avoid the heap's repeated logarithmic reordering and are the more direct fit.