AlgoMaster Logo

Sort Characters By Frequency

Last Updated: March 29, 2026

medium
4 min read

Understanding the Problem

We need to rearrange the characters in a string so that the most frequent characters come first. Characters with the same frequency can appear in any order relative to each other, but all occurrences of the same character must be grouped together. So "aaabb" is valid for a string where 'a' appears 3 times and 'b' appears 2 times, but "ababa" is not.

A couple of things to notice. First, the problem is case-sensitive: 'A' and 'a' are different characters. Second, digits are also valid characters in the input. Third, when two characters have the same frequency, any ordering between them is acceptable. This flexibility is important because it means we do not need a stable sort or any tiebreaking logic.

The core task boils down to two steps: count how often each character appears, then build a result string by outputting characters from most frequent to least frequent.

Key Constraints:

  • 1 <= s.length <= 5 * 10^5 → With up to 500,000 characters, we need O(n log n) or better. An O(n^2) approach would risk timing out.
  • s consists of uppercase and lowercase English letters and digits → At most 62 distinct characters (26 + 26 + 10). This is a small, fixed alphabet, which opens the door to bucket sort.

Approach 1: Sorting

Intuition

The most straightforward plan: count how many times each character appears, then sort the distinct characters by their frequency in descending order, and finally build the result by repeating each character the appropriate number of times.

Since the alphabet is small (at most 62 distinct characters), sorting is extremely cheap. The expensive part is just the counting and the string building, both of which are O(n). So this approach is already quite efficient despite using a sort.

Algorithm

  1. Build a frequency map that counts how many times each character appears in the string.
  2. Collect the entries of the frequency map (each entry is a character-count pair).
  3. Sort these entries by count in descending order.
  4. Build the result string by appending each character repeated by its count.

Example Walkthrough

1Start: scan string to count character frequencies
0
t
scan
1
r
2
e
3
e
1/6
1Frequency map is empty
1/6

Code

The sorting approach works well, but we can also use a max-heap to extract characters in frequency order, which is a natural fit for "give me the next most frequent" problems.

Approach 2: Max-Heap (Priority Queue)

Intuition

Instead of sorting all entries at once, we can use a max-heap (priority queue) to always extract the character with the highest frequency. Count the frequencies first, push all character-frequency pairs into a max-heap ordered by frequency, then repeatedly extract the maximum and append that character to the result.

This is a natural fit because the heap gives us the most frequent remaining character in O(log k) time, and we only have at most 62 distinct characters. The approach is also flexible: if the problem changed to "give me only the top 3 most frequent characters," a heap would shine.

Algorithm

  1. Build a frequency map counting each character's occurrences.
  2. Push every (frequency, character) pair into a max-heap.
  3. While the heap is not empty:
    • Extract the pair with the highest frequency.
    • Append that character repeated by its frequency to the result.
  4. Return the result string.

Example Walkthrough

1Start: count frequencies → t:1, r:1, e:2
0
t
1
r
2
e
3
e
1/6
1Heap is empty. Counting frequencies first.
[]
1/6

Code

Both approaches use comparison-based sorting on the distinct characters. But since every character's frequency is an integer between 1 and n, we can use bucket sort to avoid comparisons entirely and achieve a true O(n) solution.

Approach 3: Bucket Sort (Optimal)

Intuition

Here is the key insight: the maximum possible frequency of any character is n (the length of the string). So we can create an array of n+1 buckets, where bucket[i] holds all characters that appear exactly i times. After populating the buckets, we just walk from bucket[n] down to bucket[1], appending each character the appropriate number of times. No sorting, no heap, just array indexing.

This is a classic application of bucket sort for problems where the sort key is a bounded integer. Since frequencies range from 1 to n, and we have at most 62 distinct characters spread across these buckets, the entire process is O(n).

Algorithm

  1. Build a frequency map counting each character's occurrences.
  2. Create an array of buckets with size n+1 (where n is the string length). Each bucket is a list of characters.
  3. For each character in the frequency map, place it into the bucket at index equal to its frequency.
  4. Iterate through the buckets from index n down to 1. For each character in the current bucket, append it repeated by the bucket index (which is its frequency) to the result.
  5. Return the result string.

Example Walkthrough

1Start: count frequencies → A:1, a:1, b:2
0
A
1
a
2
b
3
b
1/6
1Create empty buckets[0..4] (n=4, so size n+1=5)
0
[]
1
[]
2
[]
3
[]
4
[]
1/6

Code