Bucket Sort is a sorting algorithm that:
- Divides elements into multiple buckets
- Sorts each bucket individually (often using another sorting algorithm like quicksort or just placing directly)
- Then combines all buckets into the final sorted array
It's especially efficient when input is uniformly distributed, like floating-point numbers between 0.0 and 1.0.
Let’s say we want to sort this array:
[0.42, 0.32, 0.23, 0.52, 0.25, 0.47, 0.51]
bucket[0] = []
bucket[1] = []
...
bucket[9] = []
0.42 → bucket[4]
0.32 → bucket[3]
0.23 → bucket[2]
...
void bucketSort(float[] arr) {
int n = arr.length;
List<Float>[] buckets = new List[n];
// Create empty buckets
for (int i = 0; i < n; i++) {
buckets[i] = new ArrayList<>();
}
// Add elements to respective buckets
for (float num : arr) {
int bucketIndex = (int)(num * n); // index based on range
buckets[bucketIndex].add(num);
}
// Sort individual buckets
for (List<Float> bucket : buckets) {
Collections.sort(bucket);
}
// Concatenate all buckets into original array
int index = 0;
for (List<Float> bucket : buckets) {
for (float num : bucket) {
arr[index++] = num;
}
}
}- It's not used for general-purpose sorting (like merge sort or quicksort), but
- It's very useful when:
- You have bounded values (like digits, characters, etc.)
- Or you can distribute elements into known ranges
Bonus: Bucket sort is often used in frequency-based problems (like frequencySort), where each bucket is a list of characters with a given frequency.
Awesome! Here's how you can solve the frequencySort problem using bucket sort instead of a max heap.
- Count frequency of each character.
- Create an array of buckets where index = frequency.
- Place characters into their corresponding frequency bucket.
- Traverse buckets from high to low and build the result string.
class Solution {
public String frequencySort(String s) {
Map<Character, Integer> freqMap = new HashMap<>();
// 1. Count frequency
for (char c : s.toCharArray()) {
freqMap.merge(c, 1, Integer::sum);
}
// 2. Create buckets where index = frequency
// Max frequency cannot be more than length of the string
List<Character>[] buckets = new List[s.length() + 1];
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList<>();
}
// 3. Fill the buckets
for (Map.Entry<Character, Integer> entry : freqMap.entrySet()) {
buckets[entry.getValue()].add(entry.getKey());
}
// 4. Build the result string from buckets (high freq to low)
StringBuilder sb = new StringBuilder();
for (int freq = buckets.length - 1; freq > 0; freq--) {
for (char c : buckets[freq]) {
sb.append(String.valueOf(c).repeat(freq));
}
}
return sb.toString();
}
}- No need for a PriorityQueue
- Linear time complexity in best-case: O(n) if frequencies are well-distributed
- Very clean and efficient for this type of problem
Here's a side-by-side comparison of the two approaches used in frequencySort — Heap-based vs Bucket Sort:
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Max Heap | O(n log k) (n = input size, k = distinct chars) |
O(n + k) | PriorityQueue takes log time per insertion |
| Bucket Sort | O(n) | O(n + k) | Fastest when max frequency is small |
- Counting Frequencies: O(n)
- Heap Insertion: O(k log k) where k = number of unique characters
- Building result: O(n)
- Total:
O(n + k log k)- Worst case
k = n→O(n log n)
- Worst case
- Counting Frequencies: O(n)
- Bucket creation & fill: O(k)
- Building result (traversing buckets): O(n)
- Total:
O(n + k)- Worst case
k = n, stillO(n)
- Worst case
- Both use:
HashMapfor frequency count: O(k)- Result
StringBuilder: O(n)
- Heap uses a
PriorityQueue: O(k) - Bucket sort uses
List[]of sizen+1: O(n)
| Metric | Max Heap | Bucket Sort |
|---|---|---|
| Simpler to write | ✅ | ✅ |
| Faster (in theory) | ❌ O(n log k) | ✅ O(n) |
| Real-world usage | ✅ | ✅ |
| Java 8 friendly | ✅ | ✅ |
- When values can be grouped by a small bounded integer (like frequency).
- When performance matters and overhead of a heap is avoidable.
You're right — both heap and bucket sort solve the problem, so here's a breakdown of when and why to prefer one over the other.
-
You care about custom sorting logic
- Heap is flexible — you can sort by frequency, lexical order, or even combine criteria.
- For example: "Sort by frequency desc, but if same freq, use alphabetical order" — easy with a heap comparator.
-
The range of values (frequencies) is unknown or unbounded
- In bucket sort, you need to allocate an array of size = max frequency. If max frequency is 1 million, that’s inefficient.
-
You only need top K frequent elements
- Heap shines in problems like Top K Frequent Elements, because it gives you the top items without full sorting.
-
You know frequencies are bounded and small
- Like in your problem: max frequency ≤ string length
-
You want linear time
- No need to insert into a
PriorityQueuewhich takesO(log k)per insertion.
- No need to insert into a
-
You don’t need any fancy custom sorting inside buckets
- If frequency is the only criteria, just group and output.
In this specific problem (frequencySort):
- Bucket sort is more efficient, because:
- You already know the max frequency ≤
s.length() - No extra sorting logic is needed beyond frequency
- You already know the max frequency ≤
- Heap is more flexible, but overkill here
✔️ Use heap when you need dynamic, top-k, or complex sorting logic.
✔️ Use bucket sort when frequency is small, bounded, and simple.