medium Sort Characters By Frequency
Problem Statement
Given a string, arrange its characters in descending order based on the frequency of each character. If two characters have the same frequency, their relative order in the output string can be arbitrary.
Example
- Input: s = "trersesess"
- Expected Output: "sssseeerrt"
- Justification: The character
sappears four times,ethree times,rtwo times andtonly once. All characters are sorted based on their frequnecy.
- Input: s = "banana"
- Expected Output: "aaannb".
- Justification: The character 'a' appears three times, 'n' twice and 'b' once.
- Input: s = "abb"
- Expected Output: "bba"
- Justification: The character
b appears twice anda` only once.
Constraints:
- 1 <= s.length <= 5 * 105
sconsists of uppercase and lowercase English letters and digits.
Try it yourself
Try solving this question here:
✅ Solution Sort Characters By Frequency
Problem Statement
Given a string, arrange its characters in descending order based on the frequency of each character. If two characters have the same frequency, their relative order in the output string can be arbitrary.
Example
- Input: s = "trersesess"
- Expected Output: "sssseeerrt"
- Justification: The character
sappears four times,ethree times,rtwo times andtonly once. All characters are sorted based on their frequnecy.
- Input: s = "banana"
- Expected Output: "aaannb".
- Justification: The character 'a' appears three times, 'n' twice and 'b' once.
- Input: s = "abb"
- Expected Output: "bba"
- Justification: The character
b appears twice anda` only once.
Constraints:
- 1 <= s.length <= 5 * 105
sconsists of uppercase and lowercase English letters and digits.
Solution
The solution involves three key steps. First, count the frequency of each character in the string. This can be done using a hashmap where keys are characters and values are their respective counts. Second, build a max heap (priority queue) where each element is a character, and the heap is sorted based on the frequency of characters. The character with the highest frequency will be at the top. Lastly, construct the result string by repeatedly removing the top element of the heap (the most frequent character) and appending it to the result string until the heap is empty. This process ensures that characters are added to the result string in descending order of their frequencies.
Step-by-Step Algorithm
-
Building a Frequency Map: Begin by iterating over the characters in the string. As you traverse, construct a frequency map (or dictionary) that associates each character with its occurrence count.
-
Constructing a Max Heap: Next, utilize the frequency map to build a max heap. Each item in this heap will be a character-frequency pair, but the heap will be organized based on the frequencies. This ensures that characters with higher frequencies will reside at the top of the heap.
-
Building the Result String: Start an iterative process where you repeatedly pop the heap to get the character with the highest current frequency. Each time you pop, append the character to the result string as many times as its frequency indicates. This ensures that characters with higher frequencies are placed before those with lower frequencies in the result.
-
Completion: Once the heap is exhausted, your result string is complete, containing characters sorted by their frequencies in descending order. This string is then returned as the output.
The utilization of a max heap is crucial here, as it allows efficient retrieval of the character with the highest frequency at any given point in the process, ensuring the desired order in the result string.
Algorithm Walkthrough
Let's walk through the code step by step for the input string s = "trersesess".
1. Build the Frequency Map
- Initialize an empty
frequencyMapto store the frequency of each character. - Iterate over each character in the string
sand update thefrequencyMap:- 't' →
frequencyMap['t'] = 1 - 'r' →
frequencyMap['r'] = 1 - 'e' →
frequencyMap['e'] = 1 - 'r' →
frequencyMap['r'] = 2 - 's' →
frequencyMap['s'] = 1 - 'e' →
frequencyMap['e'] = 2 - 's' →
frequencyMap['s'] = 2 - 'e' →
frequencyMap['e'] = 3 - 's' →
frequencyMap['s'] = 3 - 's' →
frequencyMap['s'] = 4
- 't' →
Final frequencyMap:
{'t': 1, 'r': 2, 'e': 3, 's': 4}
2. Add Entries to the Max Heap
- Create a max heap (
maxHeap) to store the characters based on their frequency in descending order. - Add all entries from the
frequencyMapto themaxHeap. - The heap will organize elements based on their frequency:
- Insert ('t', 1) → Heap:
[('t', 1)] - Insert ('r', 2) → Heap:
[('r', 2), ('t', 1)] - Insert ('e', 3) → Heap:
[('e', 3), ('t', 1), ('r', 2)] - Insert ('s', 4) → Heap:
[('s', 4), ('e', 3), ('r', 2), ('t', 1)]
- Insert ('t', 1) → Heap:
Heap organization ensures that the character with the highest frequency is always at the top.
3. Build the Result String
- Initialize an empty
StringBuildernamedresultto construct the final output string. - Repeatedly remove the top element from the
maxHeapand append it toresultas many times as its frequency:- Poll ('s', 4) → Append "ssss" to
result→result = "ssss" - Poll ('e', 3) → Append "eee" to
result→result = "sssseee" - Poll ('r', 2) → Append "rr" to
result→result = "sssseeerr" - Poll ('t', 1) → Append "t" to
result→result = "sssseeerrt"
- Poll ('s', 4) → Append "ssss" to
4. Return the Result
- Convert the
resultStringBuilder to a string and return it.
Final output for the input string "trersesess" is "sssseeerrt". The characters appear in descending order of their frequency.
Code
Here is the code for this algorithm:
import java.util.*;
public class Solution {
public String frequencySort(String s) {
// Build the frequency map
Map<Character, Integer> frequencyMap = new HashMap<>();
for (char c : s.toCharArray()) {
frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1);
}
// Add entries to the max heap
PriorityQueue<Map.Entry<Character, Integer>> maxHeap = new PriorityQueue<>(
(a, b) -> b.getValue() - a.getValue()
);
maxHeap.addAll(frequencyMap.entrySet());
// Build the result string
StringBuilder result = new StringBuilder();
while (!maxHeap.isEmpty()) {
Map.Entry<Character, Integer> entry = maxHeap.poll();
for (int i = 0; i < entry.getValue(); i++) {
result.append(entry.getKey());
}
}
return result.toString();
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.frequencySort("programming")); // Expected: gggrrmmiapo
System.out.println(sol.frequencySort("aab")); // Expected: aab or baa
System.out.println(sol.frequencySort("apple")); // Expected: pplea
}
}
Time Complexity:
-
Counting the Frequency of Characters: We traverse the string of length
nonce to count the frequency of each character. This process has a time complexity of. -
Inserting into a Heap/Min-Priority Queue: For every unique character in the input string, we insert it into a priority queue based on its frequency. The insertion of a single element into a priority queue takes
time where mis the number of unique characters in the string. In the worst case,mcan be the size of the character set, but it's much smaller thannin most practical scenarios. The overall time for this step is. -
Building the Result String: While the heap is not empty, we pop the most frequent character and append it to our result string. Popping from a heap takes
time, and we do it mtimes. Appending characters to the result string can be done intime. Therefore, this step overall is .
Combining all steps, the dominant factor for time complexity is the process of pushing and popping from the priority queue. Therefore, the overall time complexity is m is often much smaller than n (especially for large strings), the time complexity can be approximated as
Space Complexity:
-
Storing Frequencies: We use a hashmap or an equivalent data structure to store the frequency of each character. In the worst case, this will take up space proportional to the number of unique characters,
m. This gives us a space complexity of. -
Priority Queue: We also use a priority queue to store unique characters based on their frequency. In the worst case, this queue can have all unique characters, adding another
space. -
Result String: We create a new string to store our result. In the worst case, this string will be of length
n, the length of the original string, giving us a space complexity of.
Adding up all the components, the total space complexity is
To summarize:
- Time Complexity:
- Space Complexity:
An Alternate Approach (Using the Bucket Sort)
In the first approach, we solved the problem using a heap, resulting in a time complexity of n is the length of the string and m is the number of unique characters. While this approach is efficient, it involves the overhead of maintaining a heap, which can be optimized further.
We can use the bucket sort algorithm to solve the problem in linear time complexity. To use bucket sort, we first count the frequency of each character in the string. Then, we create an array of buckets where each bucket index corresponds to a specific frequency. Characters are placed into the appropriate bucket based on their frequency. Finally, we build the output string by iterating through the buckets in reverse order, ensuring that characters with higher frequencies appear first in the result.
This bucket sort approach is more efficient than the heap-based method, reducing the time complexity to
Step-by-Step Algorithm
-
Count the Frequency of Each Character:
- Create a
frequencyMap(a dictionary) to store the frequency of each character in the string. - Iterate over each character in the string
s. For each character:- If the character is already in the
frequencyMap, increment its count. - If the character is not in the
frequencyMap, add it with an initial count of 1.
- If the character is already in the
- This step ensures that we know how many times each character appears in the string.
- Create a
-
Determine the Maximum Frequency:
- Initialize a variable
maxFrequencyto zero. - Iterate over the values in
frequencyMap(i.e., the frequencies of the characters):- Update
maxFrequencyto be the maximum value found during the iteration.
- Update
- This step helps determine the size of the buckets needed in the next step.
- Initialize a variable
-
Create Buckets Based on Frequency:
- Create a list
buckets, where each index represents a possible frequency (from 0 tomaxFrequency). - Each element in
bucketsis an empty list (or array) that will store characters having the corresponding frequency. - This step sets up the structure to sort characters based on their frequency.
- Create a list
-
Place Characters in the Corresponding Buckets:
- Iterate over the
frequencyMap. For each character and its frequency:- Append the character to the bucket at the index corresponding to its frequency in
buckets.
- Append the character to the bucket at the index corresponding to its frequency in
- This step groups characters by their frequency, facilitating the sorting process.
- Iterate over the
-
Build the Output String:
- Initialize an empty string builder
resultto construct the final output string. - Iterate over the
bucketslist in reverse order (from the highest frequency to the lowest):- For each bucket (which contains characters with the same frequency):
- For each character in the bucket:
- Append the character to
resultthe number of times equal to its frequency (index of the bucket).
- Append the character to
- For each character in the bucket:
- For each bucket (which contains characters with the same frequency):
- This step ensures that characters with higher frequencies are placed first in the final string.
- Initialize an empty string builder
-
Return the Result:
- Convert the string builder
resultto a string and return it as the final output.
- Convert the string builder
Algorithm Walkthrough
Let's go through each step of the algorithm with the string s = "trersesess".
-
Check for Empty Input:
- The input string
sis"trersesess", which is not empty, so we proceed to the next step.
- The input string
-
Count the Frequency of Each Character:
- Initialize
frequencyMapas an empty map. - Iterate through the string and update
frequencyMap:- 't' →
frequencyMap['t'] = 1 - 'r' →
frequencyMap['r'] = 1 - 'e' →
frequencyMap['e'] = 1 - 'r' →
frequencyMap['r'] = 2 - 's' →
frequencyMap['s'] = 1 - 'e' →
frequencyMap['e'] = 2 - 's' →
frequencyMap['s'] = 2 - 'e' →
frequencyMap['e'] = 3 - 's' →
frequencyMap['s'] = 3 - 's' →
frequencyMap['s'] = 4
- 't' →
- The final
frequencyMapis{'t': 1, 'r': 2, 'e': 3, 's': 4}.
- Initialize
-
Determine the Maximum Frequency:
- Initialize
maxFrequency = 0. - Iterate through the frequencies in
frequencyMapto get the maximum frequency. - The maximum frequency
maxFrequencyis4.
- Initialize
-
Create Buckets Based on Frequency:
- Create a list
bucketswith 5 elements (0 tomaxFrequency), each initialized as an empty list. buckets = [[], [], [], [], []]
- Create a list
-
Place Characters in the Corresponding Buckets:
- Iterate over
frequencyMap:- 't' with frequency 1 → Place 't' in
buckets[1]. - 'r' with frequency 2 → Place 'r' in
buckets[2]. - 'e' with frequency 3 → Place 'e' in
buckets[3]. - 's' with frequency 4 → Place 's' in
buckets[4].
- 't' with frequency 1 → Place 't' in
- The final
bucketslist is[[], ['t'], ['r'], ['e'], ['s']].
- Iterate over
-
Build the Output String:
- Initialize an empty string builder
result. - Iterate over
bucketsin reverse order (from 4 to 1):- For
buckets[4]→ Contains 's', append 'ssss' toresult. - For
buckets[3]→ Contains 'e', append 'eee' toresult. - For
buckets[2]→ Contains 'r', append 'rr' toresult. - For
buckets[1]→ Contains 't', append 't' toresult.
- For
- The final
resultis'sssseeerrt'.
- Initialize an empty string builder
-
Return the Result:
- Convert
resultto a string and return it:"sssseeerrt".
- Convert
The final output for the input string "trersesess" is "sssseeerrt".
Code
Here is the code for this algorithm:
import java.util.*;
class Solution {
public String frequencySort(String s) {
// Step 1: Count the frequency of each character.
Map<Character, Integer> frequencyMap = new HashMap<>();
for (char character : s.toCharArray()) {
frequencyMap.put(character, frequencyMap.getOrDefault(character, 0) + 1);
}
// Step 2: Find the maximum frequency to determine the size of buckets.
int maxFrequency = Collections.max(frequencyMap.values());
// Step 3: Create buckets, where index represents the frequency.
List<List<Character>> buckets = new ArrayList<>();
for (int i = 0; i <= maxFrequency; i++) {
buckets.add(new ArrayList<>());
}
// Step 4: Place each character into the corresponding bucket based on its frequency.
for (Map.Entry<Character, Integer> entry : frequencyMap.entrySet()) {
char character = entry.getKey();
int frequency = entry.getValue();
buckets.get(frequency).add(character);
}
// Step 5: Build the output string by iterating through the buckets in reverse order.
StringBuilder result = new StringBuilder();
for (int i = buckets.size() - 1; i >= 1; i--) {
for (char character : buckets.get(i)) {
for (int j = 0; j < i; j++) {
result.append(character);
}
}
}
return result.toString();
}
public static void main(String[] args) {
Solution solution = new Solution();
// Test Case 1
String s1 = "trersesess";
System.out.println("Output: " + solution.frequencySort(s1));
// Expected: "sssseeerrt"
// Test Case 2
String s2 = "banana";
System.out.println("Output: " + solution.frequencySort(s2));
// Expected: "aaannb"
// Test Case 3
String s3 = "abb";
System.out.println("Output: " + solution.frequencySort(s3));
// Expected: "bba"
}
}
Complexity Analysis
Time Complexity
-
Counting Character Frequencies: This step involves iterating through the input string, which takes
time, where nis the length of the string. -
Creating and Filling Buckets: The frequency map is processed to create and fill the buckets. This involves iterating through the map and adding elements to the buckets, taking
time. -
Building the Output String: The final string is built by iterating through the buckets and appending characters based on their frequency, which also takes
time.
Overall Time Complexity: The overall time complexity is
Space Complexity
-
Frequency Map: The space required for the frequency map is
, where mis the number of unique characters in the string. -
Buckets: The space required for the buckets is
in the worst case when the string contains only a single character.
Overall Space Complexity: The overall space complexity is
🤖 Don't fully get this? Learn it with Claude
Stuck on Sort Characters By Frequency? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Sort Characters By Frequency** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Sort Characters By Frequency** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Sort Characters By Frequency**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Sort Characters By Frequency**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.