Hubbry Logo
Bucket sortBucket sortMain
Open search
Bucket sort
Community hub
Bucket sort
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Contribute something
Bucket sort
Bucket sort
from Wikipedia
Bucket sort
ClassSorting algorithm
Data structureArray
Worst-case performance
Average performance, where k is the number of buckets. .
Worst-case space complexity
Elements are distributed among bins
Then, elements are sorted within each bin

Bucket sort, or bin sort, is a sorting algorithm that works by distributing the elements of an array into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a distribution sort, a generalization of pigeonhole sort that allows multiple keys per bucket, and is a cousin of radix sort in the most-to-least significant digit flavor. Bucket sort can be implemented with comparisons and therefore can also be considered a comparison sort algorithm. The computational complexity depends on the algorithm used to sort each bucket, the number of buckets to use, and whether the input is uniformly distributed.

Bucket sort works as follows:

  1. Set up an array of initially empty "buckets".
  2. Scatter: Go over the original array, putting each object in its bucket.
  3. Sort each non-empty bucket.
  4. Gather: Visit the buckets in order and put all elements back into the original array.

Pseudocode

[edit]
function bucketSort(array, k) is
    buckets ← new array of k empty lists
    M ← 1 + the maximum key value in the array
    for i = 0 to length(array) do
        insert array[i] into buckets[floor(k × array[i] / M)]
    for i = 0 to k do 
        nextSort(buckets[i])
    return the concatenation of buckets[0], ...., buckets[k]

Let array denote the array to be sorted and k denote the number of buckets to use. One can compute the maximum key value in linear time by iterating over all the keys once. The floor function must be used to convert a floating number to an integer ( and possibly casting of datatypes too ). The function nextSort is a sorting function used to sort each bucket. Conventionally, insertion sort is used due to its relatively high performance with small numbers of elements, but other algorithms could be used as well, such as selection sort or merge sort. Using bucketSort itself as nextSort produces a relative of radix sort; in particular, the case n = 2 corresponds to quicksort (although potentially with poor pivot choices).

Analysis

[edit]

Worst-case analysis

[edit]

When the input contains several keys that are close to each other (clustering), those elements are likely to be placed in the same bucket, which results in some buckets containing more elements than average. The worst-case scenario occurs when all the elements are placed in a single bucket. The overall performance would then be dominated by the algorithm used to sort each bucket, for example insertion sort or comparison sort algorithms, such as merge sort.

Average-case analysis

[edit]

Consider the case that the input is uniformly distributed. The first step, which is initialize the buckets and find the maximum key value in the array, can be done in time. If division and multiplication can be done in constant time, then scattering each element to its bucket also costs . Assume insertion sort is used to sort each bucket, then the third step costs , where is the length of the bucket indexed . Since we are concerning the average time, the expectation has to be evaluated instead. Let be the random variable that is if element is placed in bucket , and otherwise. We have . Therefore,

The last line separates the summation into the case and the case . Since the chance of an object distributed to bucket is , is 1 with probability and 0 otherwise.

With the summation, it would be

Finally, the complexity would be .

The last step of bucket sort, which is concatenating all the sorted objects in each bucket, requires time. Therefore, the total complexity is . Note that if k is chosen to be , then bucket sort runs in average time, given a uniformly distributed input.[1]

Optimizations

[edit]

A common optimization is to put the unsorted elements of the buckets back in the original array first, then run insertion sort over the complete array; because insertion sort's runtime is based on how far each element is from its final position, the number of comparisons remains relatively small, and the memory hierarchy is better exploited by storing the list contiguously in memory.[2]

If the input distribution is known or can be estimated, buckets can often be chosen which contain constant density (rather than merely having constant size). This allows average time complexity even without uniformly distributed input.

Variants

[edit]

Generic bucket sort

[edit]

The most common variant of bucket sort operates on a list of n numeric inputs between zero and some maximum value M and divides the value range into b buckets each of size M/b. If each bucket is sorted using insertion sort, the sort can be shown to run in expected linear time (where the average is taken over all possible inputs).[3] However, the performance of this sort degrades with clustering; if many values occur close together, they will all fall into a single bucket and be sorted slowly. This performance degradation is avoided in the original bucket sort algorithm by assuming that the input is generated by a random process that distributes elements uniformly over the interval [0,1).[1]

ProxmapSort

[edit]

Similar to generic bucket sort as described above, ProxmapSort works by dividing an array of keys into subarrays via the use of a "map key" function that preserves a partial ordering on the keys; as each key is added to its subarray, insertion sort is used to keep that subarray sorted, resulting in the entire array being in sorted order when ProxmapSort completes. ProxmapSort differs from bucket sorts in its use of the map key to place the data approximately where it belongs in sorted order, producing a "proxmap" — a proximity mapping — of the keys.

Histogram sort

[edit]

Another variant of bucket sort known as histogram sort or counting sort adds an initial pass that counts the number of elements that will fall into each bucket using a count array.[4] Using this information, the array values can be arranged into a sequence of buckets in-place by a sequence of exchanges, leaving no space overhead for bucket storage.[failed verification]

Postman's sort

[edit]

The Postman's sort is a variant of bucket sort that takes advantage of a hierarchical structure of elements, typically described by a set of attributes. This is the algorithm used by letter-sorting machines in post offices: mail is sorted first between domestic and international; then by state, province or territory; then by destination post office; then by routes, etc. Since keys are not compared against each other, sorting time is O(cn), where c depends on the size of the key and number of buckets. This is similar to a radix sort that works "top down," or "most significant digit first."[5]

Shuffle sort

[edit]

The shuffle sort[6] is a variant of bucket sort that begins by removing the first 1/8 of the n items to be sorted, sorts them recursively, and puts them in an array. This creates n/8 "buckets" to which the remaining 7/8 of the items are distributed. Each "bucket" is then sorted, and the "buckets" are concatenated into a sorted array.

Comparison with other sorting algorithms

[edit]

Bucket sort can be seen as a generalization of counting sort; in fact, if each bucket has size 1 then bucket sort degenerates to counting sort. The variable bucket size of bucket sort allows it to use O(n) memory instead of O(M) memory, where M is the number of distinct values; in exchange, it gives up counting sort's O(n + M) worst-case behavior.

Bucket sort with two buckets is effectively a version of quicksort where the pivot value is always selected to be the middle value of the value range. While this choice is effective for uniformly distributed inputs, other means of choosing the pivot in quicksort such as randomly selected pivots make it more resistant to clustering in the input distribution.

The n-way mergesort algorithm also begins by distributing the list into n sublists and sorting each one; however, the sublists created by mergesort have overlapping value ranges and so cannot be recombined by simple concatenation as in bucket sort. Instead, they must be interleaved by a merge algorithm. However, this added expense is counterbalanced by the simpler scatter phase and the ability to ensure that each sublist is the same size, providing a good worst-case time bound.

Top-down radix sort can be seen as a special case of bucket sort where both the range of values and the number of buckets is constrained to be a power of two. Consequently, each bucket's size is also a power of two, and the procedure can be applied recursively. This approach can accelerate the scatter phase, since we only need to examine a prefix of the bit representation of each element to determine its bucket.

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Bucket sort is a sorting algorithm that partitions an array of elements into several buckets, sorts the elements within each bucket individually (typically using insertion sort), and then concatenates the sorted buckets to produce the final sorted output. It is designed primarily for sorting real numbers that are uniformly and independently distributed over the interval [0, 1). The algorithm begins by creating n buckets, where n is the number of input elements, each corresponding to a subinterval of length 1/n within [0, 1). For each input number x, it computes the bucket index as floor(n * x) and places x into that bucket, ensuring that elements in earlier buckets are smaller than those in later ones. After distribution, each non-empty bucket is sorted using insertion sort, which is efficient for small lists, and the sorted contents of the buckets are then concatenated from left to right. Assuming uniform distribution, the expected number of elements per bucket follows a binomial distribution, leading to an expected running time of O(n) for n elements. However, in the worst case—such as when all elements fall into a single bucket—the time complexity is Θ(n²) due to the quadratic behavior of insertion sort on that bucket. Bucket sort requires O(n) extra space for the buckets and is a distribution-based method rather than a pure comparison sort, though the internal bucket sorting relies on comparisons. It is most efficient when the input meets the uniform distribution assumption and can be adapted for other ranges by scaling, but performs poorly on clustered or adversarial data.

Overview

Algorithm Description

Bucket sort is a distribution-based sorting algorithm that assumes the input elements are uniformly distributed over a known range, such as the interval [0, 1). The algorithm proceeds in four primary steps. First, an array of n empty buckets is created, where n is the number of elements to sort. Second, each input element x is distributed into a bucket by calculating its index as nx\lfloor n \cdot x \rfloor for values in [0, 1), or more generally nxmax\lfloor n \cdot \frac{x}{max} \rfloor when the range is [0, max]. Third, each non-empty bucket is sorted individually, often using an efficient algorithm like insertion sort suitable for small lists. Finally, the sorted buckets are concatenated in order to produce the fully sorted output array. Under the uniform distribution assumption, elements are evenly spread across the buckets, resulting in approximately one element per bucket on average and minimizing the work required for intra-bucket sorting. This approach is typically applied to floating-point numbers or scenarios where the input range is known and uniformity holds, enabling linear-time performance in expectation.

Key Assumptions and Prerequisites

Bucket sort relies on the assumption that the input elements are uniformly or nearly uniformly distributed across a known range to achieve balanced distribution into buckets, ensuring that each bucket receives approximately the same number of elements on average. This uniform distribution is crucial because it allows the algorithm to partition the data evenly, preventing any single bucket from becoming disproportionately large. A key prerequisite is the knowledge of the input's minimum and maximum values, which define the overall range and enable the determination of bucket boundaries. Without this range information, setting appropriate bucket intervals becomes impossible, rendering the algorithm ineffective for direct application. The algorithm is particularly suited for sorting real numbers within a bounded interval, such as [0, 1), where elements can be mapped to buckets based on their fractional values. For integer inputs, adaptations like counting sort may be more appropriate if the range is discrete and known, but bucket sort's flexibility with continuous values highlights its limitations when the range is unbounded or unknown, as dynamic range estimation would be required. Implementers need a basic understanding of array structures for storing buckets and familiarity with stable sorting methods, such as insertion sort, which is commonly used to sort elements within individual buckets. Non-uniform distributions can lead to skewed buckets, where most elements fall into one or a few buckets— for instance, all inputs concentrating in a single bucket—resulting in degraded performance as the sorting reduces to handling an unbalanced load.

Implementation

Pseudocode

The standard bucket sort algorithm distributes elements of an input array into buckets based on their values, sorts each bucket individually, and concatenates the results to produce a sorted output. This pseudocode assumes an input array AA of nn real numbers uniformly and independently distributed in the interval [0,1)[0, 1), with the number of buckets kk set to nn.

BUCKET-SORT(A) 1 n ← length[A] 2 create n empty lists B[0], B[1], ..., B[n-1] 3 for i ← 0 to n-1 do 4 bucket_index ← floor(n * A[i]) 5 insert A[i] at the end of list B[bucket_index] 6 for i ← 0 to n-1 do 7 sort list B[i] using insertion sort 8 concatenate lists B[0], B[1], ..., B[n-1] together in order (this is the output)

BUCKET-SORT(A) 1 n ← length[A] 2 create n empty lists B[0], B[1], ..., B[n-1] 3 for i ← 0 to n-1 do 4 bucket_index ← floor(n * A[i]) 5 insert A[i] at the end of list B[bucket_index] 6 for i ← 0 to n-1 do 7 sort list B[i] using insertion sort 8 concatenate lists B[0], B[1], ..., B[n-1] together in order (this is the output)

In the above pseudocode, the input is denoted by array AA of size nn, while BB represents an array of nn initially empty lists acting as buckets; the output is the concatenated sequence from the sorted buckets. The key distribution step (lines 3–5) computes the bucket index using the formula nA\lfloor n \cdot A \rfloor, which maps each element to one of the nn equally sized subintervals of [0,1)[0, 1) under the uniform distribution assumption. For inputs in a general range [0,maxvalue)[0, \max_value), the bucket index formula generalizes to n\key/maxvalue\lfloor n \cdot \key / \max_value \rfloor, where \key\key is the current element, ensuring even distribution across buckets when values are uniformly spread. The sorting step (lines 6–7) applies insertion sort to each bucket list BB, as it performs efficiently on the small, expected sizes of these lists. Empty buckets are handled naturally in the sorting loop, requiring no special action since sorting an empty list incurs negligible cost. The final concatenation (line 8) merges the sorted buckets sequentially to form the overall sorted array. The buckets may alternatively be sorted recursively by invoking bucket sort on each non-empty BB, though this variant demands provisions to terminate recursion for small or degenerate cases.

Worked Example

To illustrate the bucket sort algorithm, consider an unsorted array of seven floating-point numbers uniformly distributed in the range [0, 1): [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21]. For this example, select 5 buckets, each spanning an equal interval of 0.2, determined by computing the bucket index as the floor of the element value multiplied by the number of buckets (i.e., index = ⌊value × 5⌋). The elements are distributed as follows:
Bucket IndexRangeElements Distributed
0[0.0, 0.2)0.17
1[0.2, 0.4)0.39, 0.26, 0.21
2[0.4, 0.6)(empty)
3[0.6, 0.8)0.78, 0.72
4[0.8, 1.0)0.94
Each non-empty bucket is then sorted using insertion sort. For bucket 1, insertion sort rearranges [0.39, 0.26, 0.21] by inserting 0.26 before 0.39 and then 0.21 before both, yielding [0.21, 0.26, 0.39]; the other buckets require minimal swaps or none. The sorted buckets are:
Bucket IndexSorted Elements
0[0.17]
1[0.21, 0.26, 0.39]
2(empty)
3[0.72, 0.78]
4[0.94]
Finally, concatenate the sorted buckets in order to produce the fully sorted array: [0.17, 0.21, 0.26, 0.39, 0.72, 0.78, 0.94]. In this case, the uniform distribution of inputs results in relatively balanced bucket sizes (most containing 1–3 elements), which supports the algorithm's efficiency under its key assumption.

Performance Analysis

Time Complexity

The time complexity of bucket sort depends on the distribution of input elements and the number of buckets kk, assuming each bucket is sorted using an O(m2)O(m^2) algorithm like insertion sort, where mm is the number of elements in a bucket. In the worst case, all nn elements fall into a single bucket, reducing the algorithm to sorting nn elements with insertion sort, yielding O(n2)O(n^2) time. This occurs when the input is non-uniform and clustered, such as all keys mapping to the same interval. The average case assumes a uniform distribution of nn elements over the range [0,1)[0, 1), with kk buckets (typically k=nk = n). The time is O(n+k)O(n + k), derived as follows: distributing elements into buckets takes O(n)O(n) time, concatenating sorted buckets takes O(n)O(n) time, and sorting within buckets totals O(n)O(n) expected time. More precisely, the expected time satisfies E[T(n)]=O(n)+i=1kE[T(ni)],E[T(n)] = O(n) + \sum_{i=1}^k E[T(n_i)], where nin_i is the number of elements in bucket ii, and E[ni2]=21/n2E[n_i^2] = 2 - 1/n \approx 2 under uniformity, so the sorting cost sums to O(n)O(n). With k=nk = n, this simplifies to linear time overall. In the best case, each bucket contains at most one element (e.g., perfectly uniform with knk \geq n), requiring no intra-bucket sorting beyond trivial checks, resulting in O(n)O(n) time dominated by distribution and concatenation. The choice of kk influences performance; setting knk \approx n optimizes the average case for uniform data by balancing distribution and sorting costs, as larger kk increases concatenation overhead while smaller kk risks uneven buckets.

Space Complexity

Bucket sort has an overall space complexity of O(n+k)O(n + k), where nn is the number of input elements and kk is the number of buckets used. This bound accounts for the input array itself, which requires O(n)O(n) space, along with the auxiliary structures needed for the algorithm. In the worst case, all nn elements could be distributed across the kk buckets, but the total allocation remains linear in nn and kk since kk is typically chosen proportional to nn for uniform distributions. The auxiliary space primarily arises from the bucket array, implemented as an array of kk empty lists, which demands O(k)O(k) space for initialization, plus O(n)O(n) space to hold the elements once distributed, as each input element is appended to exactly one list. Concatenating the sorted buckets into the final output may require an additional O(n)O(n) temporary array if the implementation does not overwrite the input in place, though optimized versions can minimize this by reusing the original array where possible. Thus, the algorithm requires O(n+k)O(n + k) extra space beyond the input, making it unsuitable for memory-constrained environments without modifications. While an in-place variant of bucket sort is theoretically possible by carefully swapping elements within the input array to simulate buckets, standard implementations avoid this due to the complexity of managing bucket storage without auxiliary lists or arrays, opting instead for the straightforward O(n+k)O(n + k) auxiliary space. If bucket sort is implemented self-recursively—by applying the algorithm to sort each bucket rather than using a simpler method like insertion sort—the recursion depth in a balanced distribution would add O(logn)O(\log n) stack space; however, this approach is typically avoided to eliminate such overhead and simplify the design.

Optimizations

Bucket Selection Strategies

In the standard formulation of bucket sort, the number of buckets kk is chosen equal to the number of input elements nn when the keys are uniformly and independently distributed real numbers in the interval [0,1)[0, 1). This selection ensures that each bucket receives an expected constant number of elements (approximately 1 on average), allowing the subsequent sorting of individual buckets—typically using insertion sort—to take constant expected time per bucket, yielding an overall average-case time complexity of O(n)O(n). For integer keys within a known finite range [min,max][ \min, \max ], a range-based bucketing strategy sets k=maxmin+1k = \max - \min + 1, effectively reducing the algorithm to counting sort where each bucket corresponds to a specific value. However, when the range significantly exceeds nn (e.g., sparse integers over a large domain), kk is often capped at nn or a smaller value to prevent prohibitive space usage while maintaining reasonable distribution. Dynamic sizing of buckets adjusts kk based on the observed data range and input size to optimize performance; for instance, setting knk \approx \sqrt{n}
Add your contribution
Related Hubs
Contribute something
User Avatar
No comments yet.