Recent from talks
Nothing was collected or created yet.
Shellsort
View on Wikipedia![]() Shellsort with gaps 23, 10, 4, 1 in action | |
| Class | Sorting algorithm |
|---|---|
| Data structure | Array |
| Worst-case performance | O(n2) (worst known worst case gap sequence) O(n log2n) (best known worst case gap sequence)[1] |
| Best-case performance | O(n log n) (most gap sequences) O(n log2n) (best known worst-case gap sequence)[2] |
| Average performance | depends on gap sequence |
| Worst-case space complexity | О(n) total, O(1) auxiliary |
| Optimal | No |

Shellsort, also known as Shell sort or Shell's method, is an in-place comparison sort. It can be understood as either a generalization of sorting by exchange (bubble sort) or sorting by insertion (insertion sort).[3] The method starts by sorting pairs of elements far apart from each other, then progressively reducing the gap between elements to be compared. By starting with far-apart elements, it can move some out-of-place elements into the position faster than a simple nearest-neighbor exchange. The running time of Shellsort is heavily dependent on the gap sequence it uses. For many practical variants, determining their time complexity remains an open problem.
The algorithm was first published by Donald Shell in 1959, and has nothing to do with shells.[4][5]
Description
[edit]Shellsort is an optimization of insertion sort that allows the exchange of items that are far apart. The idea is to arrange the list of elements so that, starting anywhere, taking every hth element produces a sorted list. Such a list is said to be h-sorted. It can also be thought of as h interleaved lists, each individually sorted.[6] Beginning with large values of h allows elements to move long distances in the original list, reducing large amounts of disorder quickly, and leaving less work for smaller h-sort steps to do.[7] If the list is then k-sorted for some smaller integer k, then the list remains h-sorted. A final sort with h = 1 ensures the list is fully sorted at the end,[6] but a judiciously chosen decreasing sequence of h values leaves very little work for this final pass to do.
In simplistic terms, this means if we have an array of 1024 numbers, our first gap (h) could be 512. We then run through the list comparing each element in the first half to the element in the second half. Our second gap (k) is 256, which breaks the array into four sections (starting at 0, 256, 512, 768), and we make sure the first items in each section are sorted relative to each other, then the second item in each section, and so on. In practice the gap sequence could be anything, but the last gap is always 1 to finish the sort (effectively finishing with an ordinary insertion sort).
An example run of Shellsort with gaps 5, 3 and 1 is shown below.
| a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 | a11 | a12 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Input data | 62 | 83 | 18 | 53 | 07 | 17 | 95 | 86 | 47 | 69 | 25 | 28 |
| After 5-sorting | 17 | 28 | 18 | 47 | 07 | 25 | 83 | 86 | 53 | 69 | 62 | 95 |
| After 3-sorting | 17 | 07 | 18 | 47 | 28 | 25 | 69 | 62 | 53 | 83 | 86 | 95 |
| After 1-sorting | 07 | 17 | 18 | 25 | 28 | 47 | 53 | 62 | 69 | 83 | 86 | 95 |
The first pass, 5-sorting, performs insertion sort on five separate subarrays (a1, a6, a11), (a2, a7, a12), (a3, a8), (a4, a9), (a5, a10). For instance, it changes the subarray (a1, a6, a11) from (62, 17, 25) to (17, 25, 62). The next pass, 3-sorting, performs insertion sort on the three subarrays (a1, a4, a7, a10), (a2, a5, a8, a11), (a3, a6, a9, a12). The last pass, 1-sorting, is an ordinary insertion sort of the entire array (a1,..., a12).
As the example illustrates, the subarrays that Shellsort operates on are initially short; later they are longer but almost ordered. In both cases insertion sort works efficiently.
Unlike insertion sort, Shellsort is not a stable sort since gapped insertions transport equal elements past one another and thus lose their original order. It is an adaptive sorting algorithm in that it executes faster when the input is partially sorted.
Pseudocode
[edit]Using Marcin Ciura's gap sequence, with an inner insertion sort.
# Sort an array a[0...n-1].
gaps = [701, 301, 132, 57, 23, 10, 4, 1] # Ciura gap sequence
# Start with the largest gap and work down to a gap of 1
# similar to insertion sort but instead of 1, gap is being used in each step
foreach (gap in gaps)
{
# Do a gapped insertion sort for every element in gaps
# Each loop leaves a[0..gap-1] in gapped order
for (i = gap; i < n; i += 1)
{
# save a[i] in temp and make a hole at position i
temp = a[i]
# shift earlier gap-sorted elements up until the correct location for a[i] is found
for (j = i; (j >= gap) && (a[j - gap] > temp); j -= gap)
{
a[j] = a[j - gap]
}
# put temp (the original a[i]) in its correct location
a[j] = temp
}
}
Gap sequences
[edit]The question of deciding which gap sequence to use is difficult. Every gap sequence that contains 1 yields a correct sort (as this makes the final pass an ordinary insertion sort); however, the properties of thus obtained versions of Shellsort may be very different. Too few gaps slows down the passes, and too many gaps produces an overhead.
The table below compares most proposed gap sequences published so far. Some of them have decreasing elements that depend on the size of the sorted array (N). Others are increasing infinite sequences, whose elements less than N should be used in reverse order.
| OEIS | General term (k ≥ 1) | Concrete gaps | Worst-case time complexity |
Author and year of publication |
|---|---|---|---|---|
| [e.g. when N = 2p] | Shell, 1959[4] | |||
| Frank & Lazarus, 1960[8] | ||||
| A000225 | Hibbard, 1963[9] | |||
| A083318 | , prefixed with 1 | Papernov & Stasevich, 1965[10] | ||
| A003586 | Successive numbers of the form (3-smooth numbers) | Pratt, 1971[1] | ||
| A003462 | , not greater than | Knuth, 1973,[3] based on Pratt, 1971[1] | ||
| A036569 | Incerpi & Sedgewick, 1985,[11] Knuth[3] | |||
| A036562 | , prefixed with 1 | Sedgewick, 1982[6] | ||
| A033622 | Sedgewick, 1986[12] | |||
| Unknown | Gonnet & Baeza-Yates, 1991[13] | |||
| A108870 | (or equivalently, ) | Unknown | Tokuda, 1992[14] (misquote per OEIS) | |
| A102549 | Unknown (experimentally derived) | Unknown | Ciura, 2001[15] | |
| A366726 | Unknown | Lee, 2021[16] | ||
| Unknown | Skean, Ehrenborg, Jaromczyk, 2023[17] | |||
| Start at 1. Successive increments are smallest prime times previous | Unknown | Glenn C. Rhoads[18] |
When the binary representation of N contains many consecutive zeroes, Shellsort using Shell's original gap sequence makes Θ(N2) comparisons in the worst case. For instance, this case occurs for N equal to a power of two when elements greater and smaller than the median occupy odd and even positions respectively, since they are compared only in the last pass.
Although it has higher complexity than the O(N log N) that is optimal for comparison sorts, Pratt's version lends itself to sorting networks and has the same asymptotic gate complexity as Batcher's bitonic sorter.
Gonnet and Baeza-Yates observed that Shellsort makes the fewest comparisons on average when the ratios of successive gaps are roughly equal to 2.2.[13] This is why their sequence with ratio 2.2 and Tokuda's sequence with ratio 2.25 prove efficient. However, it is not known why this is so. Sedgewick recommends using gaps which have low greatest common divisors or are pairwise coprime.[19][failed verification] Gaps which are odd numbers seem to work well in practice: 25% reductions have been observed by avoiding even-numbered gaps. Gaps which avoid multiples of 3 and 5 seem to produce small benefits of < 10%.[original research?]
With respect to the average number of comparisons, Ciura's sequence[15] has the best known performance; gaps greater than 701 were not determined but the sequence can be further extended according to the recursive formula .
Tokuda's sequence, defined by the simple formula , where , , can be recommended for practical applications.
If the maximum input size is small, as may occur if Shellsort is used on small subarrays by another recursive sorting algorithm such as quicksort or merge sort, then it is possible to tabulate an optimal sequence for each input size.[20][21]
Computational complexity
[edit]The following property holds: after h2-sorting of any h1-sorted array, the array remains h1-sorted.[22] Every h1-sorted and h2-sorted array is also (a1h1+a2h2)-sorted, for any nonnegative integers a1 and a2. The worst-case complexity of Shellsort is therefore connected with the Frobenius problem: for given integers h1,..., hn with gcd = 1, the Frobenius number g(h1,..., hn) is the greatest integer that cannot be represented as a1h1+ ... +anhn with nonnegative integer a1,..., an. Using known formulae for Frobenius numbers, we can determine the worst-case complexity of Shellsort for several classes of gap sequences.[23] Proven results are shown in the above table.
Mark Allen Weiss proved that Shellsort runs in O(N log N) time when the input array is in reverse order.[24]
With respect to the average number of operations, none of the proven results concerns a practical gap sequence. For gaps that are powers of two, Espelid computed this average as .[25] Knuth determined the average complexity of sorting an N-element array with two gaps (h, 1) to be .[3] It follows that a two-pass Shellsort with h = Θ(N1/3) makes on average O(N5/3) comparisons/inversions/running time. Yao found the average complexity of a three-pass Shellsort.[26] His result was refined by Janson and Knuth:[27] the average number of comparisons/inversions/running time made during a Shellsort with three gaps (ch, cg, 1), where h and g are coprime, is in the first pass, in the second pass and in the third pass. ψ(h, g) in the last formula is a complicated function asymptotically equal to . In particular, when h = Θ(N7/15) and g = Θ(N1/5), the average time of sorting is O(N23/15).
Based on experiments, it is conjectured that Shellsort with Hibbard's gap sequence runs in O(N5/4) average time,[3] and that Gonnet and Baeza-Yates's sequence requires on average 0.41N ln N (ln ln N + 1/6) element moves.[13] Approximations of the average number of operations formerly put forward for other sequences fail when sorted arrays contain millions of elements.
The graph below shows the average number of element comparisons use by various gap sequences, divided by the theoretical lower bound, i.e. log2N!. Ciuria's sequence 1, 4, 10, 23, 57, 132, 301, 701 (labelled Ci01) has been extended according to the formula .

Applying the theory of Kolmogorov complexity, Jiang, Li, and Vitányi [28] proved the following lower bound for the order of the average number of operations/running time in a p-pass Shellsort: Ω(pN1+1/p) when p ≤ log2N and Ω(pN) when p > log2N. Therefore, Shellsort has prospects of running in an average time that asymptotically grows like N logN only when using gap sequences whose number of gaps grows in proportion to the logarithm of the array size. It is, however, unknown whether Shellsort can reach this asymptotic order of average-case complexity, which is optimal for comparison sorts. The lower bound was improved by Vitányi[29] for every number of passes to where . This result implies for example the Jiang-Li-Vitányi lower bound for all -pass increment sequences and improves that lower bound for particular increment sequences. In fact all bounds (lower and upper) currently known for the average case are precisely matched by this lower bound. For example, this gives the new result that the Janson-Knuth upper bound is matched by the resulting lower bound for the used increment sequence, showing that three pass Shellsort for this increment sequence uses comparisons/inversions/running time. The formula allows us to search for increment sequences that yield lower bounds which are unknown; for example an increment sequence for four passes which has a lower bound greater than for the increment sequence . The lower bound becomes
The worst-case complexity of any version of Shellsort is of higher order: Plaxton, Poonen, and Suel showed that it grows at least as rapidly as .[30][31] Robert Cypher proved a stronger lower bound: when for all .[32]
Applications
[edit]Shellsort performs more operations and has higher cache miss ratio than quicksort. However, since it can be implemented using little code and does not use the call stack, some implementations of the qsort function in the C standard library targeted at embedded systems use it instead of quicksort. Shellsort is, for example, used in the uClibc library.[33] For similar reasons, in the past, Shellsort was used in the Linux kernel.[34]
Shellsort can also serve as a sub-algorithm of introspective sort, to sort short subarrays and to prevent a slowdown when the recursion depth exceeds a given limit. This principle is employed, for instance, in the bzip2 compressor.[35]
See also
[edit]References
[edit]- ^ a b c Pratt, Vaughan Ronald (1979). Shellsort and Sorting Networks (Outstanding Dissertations in the Computer Sciences) (PDF). Garland. ISBN 978-0-8240-4406-0. Archived (PDF) from the original on 7 September 2021.
- ^ "Shellsort & Comparisons". Archived from the original on 20 December 2019. Retrieved 14 November 2015.
- ^ a b c d e Knuth, Donald E. (1997). "Shell's method". The Art of Computer Programming. Volume 3: Sorting and Searching (2nd ed.). Reading, Massachusetts: Addison-Wesley. pp. 83–95. ISBN 978-0-201-89685-5.
- ^ a b Shell, D. L. (1959). "A High-Speed Sorting Procedure" (PDF). Communications of the ACM. 2 (7): 30–32. doi:10.1145/368370.368387. S2CID 28572656. Archived from the original (PDF) on 30 August 2017. Retrieved 18 October 2011.
- ^ Some older textbooks and references call this the "Shell–Metzner" sort after Marlene Metzner Norton, but according to Metzner, "I had nothing to do with the sort, and my name should never have been attached to it." See "Shell sort". National Institute of Standards and Technology. Retrieved 17 July 2007.
- ^ a b c Sedgewick, Robert (1998). Algorithms in C. Vol. 1 (3rd ed.). Addison-Wesley. pp. 273–281. ISBN 978-0-201-31452-6.
- ^ Kernighan, Brian W.; Ritchie, Dennis M. (1996). The C Programming Language (2nd ed.). Prentice Hall. p. 62. ISBN 978-7-302-02412-5.
- ^ Frank, R. M.; Lazarus, R. B. (1960). "A High-Speed Sorting Procedure". Communications of the ACM. 3 (1): 20–22. doi:10.1145/366947.366957. S2CID 34066017.
- ^ Hibbard, Thomas N. (1963). "An Empirical Study of Minimal Storage Sorting". Communications of the ACM. 6 (5): 206–213. doi:10.1145/366552.366557. S2CID 12146844.
- ^ Papernov, A. A.; Stasevich, G. V. (1965). "A Method of Information Sorting in Computer Memories" (PDF). Problems of Information Transmission. 1 (3): 63–75.
- ^ Incerpi, Janet; Sedgewick, Robert (1985). "Improved Upper Bounds on Shellsort" (PDF). Journal of Computer and System Sciences. 31 (2): 210–224. doi:10.1016/0022-0000(85)90042-x.
- ^ Sedgewick, Robert (1986). "A New Upper Bound for Shellsort". Journal of Algorithms. 7 (2): 159–173. doi:10.1016/0196-6774(86)90001-5.
- ^ a b c Gonnet, Gaston H.; Baeza-Yates, Ricardo (1991). "Shellsort". Handbook of Algorithms and Data Structures: In Pascal and C (2nd ed.). Reading, Massachusetts: Addison-Wesley. pp. 161–163. ISBN 978-0-201-41607-7.
Extensive experiments indicate that the sequence defined by α = 0.45454 < 5/11 performs significantly better than other sequences. The easiest way to compute ⌊0.45454n⌋ is by
(5 * n — 1)/11using integer arithmetic. - ^ Tokuda, Naoyuki (1992). "An Improved Shellsort". In van Leeuven, Jan (ed.). Proceedings of the IFIP 12th World Computer Congress on Algorithms, Software, Architecture. Amsterdam: North-Holland Publishing Co. pp. 449–457. ISBN 978-0-444-89747-3.
- ^ a b Ciura, Marcin (2001). "Best Increments for the Average Case of Shellsort" (PDF). In Freiwalds, Rusins (ed.). Proceedings of the 13th International Symposium on Fundamentals of Computation Theory. London: Springer-Verlag. pp. 106–117. ISBN 978-3-540-42487-1. Archived from the original (PDF) on 23 September 2018.
- ^ Lee, Ying Wai (21 December 2021). "Empirically Improved Tokuda Gap Sequence in Shellsort". arXiv:2112.11112 [cs.DS].
- ^ Skean, Oscar; Ehrenborg, Richard; Jaromczyk, Jerzy W. (1 January 2023). "Optimization Perspectives on Shellsort". arXiv:2301.00316 [cs.DS].
- ^ Rhoads, Glenn (1 March 2010). "Shellsort Increment Sequences". The Glenn. Retrieved 13 May 2025.
- ^ Sedgewick, Robert (1998). "Shellsort". Algorithms in C++, Parts 1–4: Fundamentals, Data Structure, Sorting, Searching. Reading, Massachusetts: Addison-Wesley. pp. 285–292. ISBN 978-0-201-35088-3.
- ^ Forshell, Olof (22 May 2018). "How to choose the lengths of my sub sequences for a shell sort?". Stack Overflow. Additional commentary at Fastest gap sequence for shell sort? (23 May 2018).
- ^ Lee, Ying Wai (21 December 2021). "Optimal Gap Sequences in Shellsort for n ≤ 16 Elements". arXiv:2112.11127 [math.CO].
- ^ Gale, David; Karp, Richard M. (April 1972). "A Phenomenon in the Theory of Sorting" (PDF). Journal of Computer and System Sciences. 6 (2): 103–115. doi:10.1016/S0022-0000(72)80016-3.
- ^ Selmer, Ernst S. (March 1989). "On Shellsort and the Frobenius Problem" (PDF). BIT Numerical Mathematics. 29 (1): 37–40. doi:10.1007/BF01932703. hdl:1956/19572. S2CID 32467267.
- ^ Weiss, Mark Allen (1989). "A good case for Shellsort". Congressus Numerantium. 73: 59–62.
- ^ Espelid, Terje O. (December 1973). "Analysis of a Shellsort Algorithm". BIT Numerical Mathematics. 13 (4): 394–400. doi:10.1007/BF01933401. S2CID 119443598. The quoted result is equation (8) on p. 399.
- ^ Yao, Andrew Chi-Chih (1980). "An Analysis of (h, k, 1)-Shellsort" (PDF). Journal of Algorithms. 1 (1): 14–50. doi:10.1016/0196-6774(80)90003-6. S2CID 3054966. STAN-CS-79-726. Archived from the original (PDF) on 4 March 2019.
- ^ Janson, Svante; Knuth, Donald E. (1997). "Shellsort with Three Increments" (PDF). Random Structures and Algorithms. 10 (1–2): 125–142. arXiv:cs/9608105. CiteSeerX 10.1.1.54.9911. doi:10.1002/(SICI)1098-2418(199701/03)10:1/2<125::AID-RSA6>3.0.CO;2-X.
- ^ Jiang, Tao; Li, Ming; Vitányi, Paul (September 2000). "A Lower Bound on the Average-Case Complexity of Shellsort" (PDF). Journal of the ACM. 47 (5): 905–911. arXiv:cs/9906008. CiteSeerX 10.1.1.6.6508. doi:10.1145/355483.355488. S2CID 3265123.
- ^ Vitányi, Paul (March 2018). "On the average-case complexity of Shellsort" (PDF). Random Structures and Algorithms. 52 (2): 354–363. arXiv:1501.06461. doi:10.1002/rsa.20737. S2CID 6833808.
- ^ Plaxton, C. Greg; Poonen, Bjorn; Suel, Torsten (24–27 October 1992). "Improved lower bounds for Shellsort" (PDF). Proceedings., 33rd Annual Symposium on Foundations of Computer Science. Vol. 33. Pittsburgh, United States. pp. 226–235. CiteSeerX 10.1.1.43.1393. doi:10.1109/SFCS.1992.267769. ISBN 978-0-8186-2900-6. S2CID 15095863.
{{cite book}}: CS1 maint: location missing publisher (link) - ^ Plaxton, C. Greg; Suel, Torsten (May 1997). "Lower Bounds for Shellsort" (PDF). Journal of Algorithms. 23 (2): 221–240. CiteSeerX 10.1.1.460.2429. doi:10.1006/jagm.1996.0825.
- ^ Cypher, Robert (1993). "A Lower Bound on the Size of Shellsort Sorting Networks". SIAM Journal on Computing. 22: 62–71. doi:10.1137/0222006.
- ^ Novoa, Manuel III. "libc/stdlib/stdlib.c". Retrieved 29 October 2014.
- ^ "kernel/groups.c". GitHub. Retrieved 5 May 2012.
- ^ Julian Seward. "bzip2/blocksort.c". Retrieved 30 March 2011.
Bibliography
[edit]- Knuth, Donald E. (1997). "Shell's method". The Art of Computer Programming. Volume 3: Sorting and Searching (2nd ed.). Reading, Massachusetts: Addison-Wesley. pp. 83–95. ISBN 978-0-201-89685-5.
- Analysis of Shellsort and Related Algorithms, Robert Sedgewick, Fourth European Symposium on Algorithms, Barcelona, September 1996.
External links
[edit]- Animated Sorting Algorithms: Shell Sort at the Wayback Machine (archived 10 March 2015) – graphical demonstration
- Shellsort with gaps 5, 3, 1 as a Hungarian folk dance
Shellsort
View on GrokipediaOverview
Description
Shellsort is a comparison-based sorting algorithm that generalizes insertion sort by allowing the comparison and exchange of elements that are far apart in the array, specifically those separated by a sequence of decreasing gaps known as h-sorts.[1] Introduced by Donald Shell in 1959, it performs a series of insertion sorts on subarrays defined by these gaps, enabling elements to move more quickly toward their final positions compared to the adjacent swaps in standard insertion sort.[1][4] The primary motivation for Shellsort stems from the limitations of insertion sort, which exhibits quadratic time complexity in the worst case due to the need to shift distant inverted elements one position at a time, leading to O(n²) operations for large inversions.[5] By preprocessing the array with larger gaps, Shellsort reduces the number of such distant inversions early in the process, bringing elements roughly into their correct relative order before applying smaller gaps and ultimately a full insertion sort with gap 1.[1] This approach leverages the adaptive nature of insertion sort on partially ordered data while mitigating its inefficiency on random inputs.[4] At a high level, the algorithm begins with a large initial gap (often around n/2, where n is the array length) and applies insertion sort to each of the independent subarrays formed by elements spaced at that gap.[5] The gap is then systematically reduced—typically by halving or following a predefined sequence—repeating the insertion sort passes on the new subarrays until the gap reaches 1, at which point the entire array is fully sorted.[1] Various gap sequences can be used, though their selection is discussed in dedicated analyses.[5] Shellsort is particularly advantageous as an in-place algorithm that requires no additional memory beyond the input array and adapts well to input data that is already partially sorted, making it efficient for moderately sized arrays in practice.[4] Its simplicity in implementation and lack of recursion further contribute to its utility in resource-constrained environments.[5] To illustrate, consider sorting the array [5, 2, 8, 1, 9] with an initial gap of 2. The subarrays are the elements at even indices (positions 0, 2, 4: [5, 8, 9], already sorted) and odd indices (positions 1, 3: [2, 1]). Applying insertion sort to the odd subarray swaps 2 and 1, resulting in [5, 1, 8, 2, 9]. This pass reduces inversions, such as moving 1 closer to its final position near the front, without resolving the full order yet—subsequent smaller gaps will refine it further.[5]History
Shellsort was invented by Donald L. Shell, an American computer scientist employed at the General Electric Company in Cincinnati, Ohio. In 1959, he published the algorithm in his seminal paper "A High-Speed Sorting Procedure" in Communications of the ACM, marking the first formal description of the method.[1] Shell developed the algorithm during his work on computing systems in the late 1950s, drawing inspiration from existing sorting techniques such as insertion sort but innovating with a series of diminishing increments to enable more efficient element exchanges across larger distances early in the process. This approach addressed limitations in simple sorts for handling larger datasets in internal memory, reflecting the computational constraints of the era.[1] Early empirical evaluations presented in Shell's paper revealed substantial performance gains over basic insertion sort, prompting its initial adoption in practical computing applications. The introduction of this adaptive strategy profoundly influenced later sorting algorithm research, establishing a foundation for exploring increment-based optimizations in comparison sorts.[1] Post-1959, the algorithm underwent significant refinements, particularly in gap sequence design to mitigate worst-case inefficiencies. A notable early improvement came in 1963 when Thomas N. Hibbard proposed a sequence of gaps following powers of 2 minus 1 (e.g., 1, 3, 7, 15, ...), which empirical studies showed could achieve better average-case bounds than Shell's original powers-of-2 decrements. Further advancements in the 1960s through 1980s, including sequences by Donald E. Knuth in 1973, continued to enhance its theoretical and practical viability.[3] In the 2020s, Shellsort remains a subject of theoretical investigation for insights into adaptive sorting behaviors, with recent work analyzing its optimization challenges and complexity under various gap configurations, including a 2025 study demonstrating expected O(n log² n) complexity using specific increment sequences.[6][7][8] It also sees occasional use in modern contexts, such as the bzip2 compression utility, where its in-place nature and avoidance of deep recursion suit resource-limited environments.[6][7]Algorithm
Core Mechanism
Shellsort operates by iteratively sorting subsets of the input array defined by a decreasing sequence of gaps, known as h-sorting phases. For a given gap size h, the array is conceptually divided into h interleaved subarrays, where each subarray consists of elements separated by h positions (i.e., elements at indices i, i+h, i+2h, ..., for i = 0 to h-1). Each of these subarrays is then independently sorted using insertion sort, which involves shifting elements within the subarray to maintain order while operating directly on the original array positions. This process begins with the largest gap h (often on the order of n/3 or similar, depending on the gap sequence) and proceeds by reducing h in subsequent iterations until h reaches 1.[1][9] The choice of gaps plays a crucial role in the algorithm's efficiency, as larger initial gaps address long-range disorder by allowing elements that are far apart to be compared and moved early, thereby reducing the number of inversions (pairs of elements out of their natural order) across the array more rapidly than a single insertion sort would. As the gaps diminish, the focus shifts to refining local order within smaller subarrays, progressively bringing the array closer to fully sorted. This staged reduction in gap sizes ensures that by the time h=1, the array is already partially ordered, minimizing the work required in the final phase. All operations—comparisons, shifts, and swaps—are performed in-place on the original array, requiring no additional storage beyond a few variables for indexing.[1][10][9] The algorithm terminates when the gap h equals 1, at which point it executes a standard insertion sort on the entire array, which benefits from the prior phases having eliminated many distant inversions. To illustrate, consider a 7-element array A = [3, 7, 1, 5, 2, 4, 6] using gaps h=3 followed by h=1 (a simplified sequence for demonstration).- Initial array: [3, 7, 1, 5, 2, 4, 6]
- Subarray 0 (indices 0,3,6): [3, 5, 6] — already sorted, no changes.
- Subarray 1 (indices 1,4): [7, 2] — insert 2 before 7, shifting: array becomes [3, 2, 1, 5, 7, 4, 6].
- Subarray 2 (indices 2,5): [1, 4] — 4 > 1, no change.
- After h=3: [3, 2, 1, 5, 7, 4, 6]
- Pass over elements, shifting out-of-order items: after full passes, the array sorts to [1, 2, 3, 4, 5, 6, 7].
Pseudocode
Shellsort is typically presented in pseudocode as an in-place sorting algorithm that operates on an array of comparable elements, generalizing insertion sort by applying it to subarrays defined by decreasing gap values until the array is fully sorted.[1][4] The standard pseudocode, assuming a simple gap sequence where gaps halve from the array length divided by 2 down to 1, follows this structure:procedure ShellSort(A: array of size n)
if n <= 1 then
return // Empty or single-element array is already sorted
end if
gap ← floor(n / 2)
while gap > 0 do
for i ← gap to n-1 do
temp ← A[i]
j ← i
while j >= gap and A[j - gap] > temp do
A[j] ← A[j - gap]
j ← j - gap
end while
A[j] ← temp
end for
gap ← floor(gap / 2)
end while
end procedure
procedure ShellSort(A: array of size n)
if n <= 1 then
return // Empty or single-element array is already sorted
end if
gap ← floor(n / 2)
while gap > 0 do
for i ← gap to n-1 do
temp ← A[i]
j ← i
while j >= gap and A[j - gap] > temp do
A[j] ← A[j - gap]
j ← j - gap
end while
A[j] ← temp
end for
gap ← floor(gap / 2)
end while
end procedure
Gap Selection Process
In Shellsort, the gap selection process involves generating a sequence of decreasing increments, known as gaps, that determine the distances between elements compared in each pass of the algorithm. The original method, proposed by Donald Shell in 1959, starts with an initial gap approximately equal to half the array size (typically ) and repeatedly halves it until reaching 1, yielding gaps such as .[1] This sequence allows the algorithm to initially sort widely spaced subsequences and progressively refine the sorting with smaller gaps. Effective gap selection relies on specific criteria to optimize performance and minimize redundant operations. Gaps in the sequence should be pairwise coprime (i.e., their greatest common divisor is 1) to prevent clustering of elements and ensure even distribution across passes, as shared factors can lead to inefficient comparisons.[16] Additionally, the sequence must be strictly decreasing to enable a refinement process where each subsequent pass builds on the partial order established by prior larger gaps, ultimately converging to a fully sorted array when the final gap is 1.[16] The gaps are integrated into the algorithm either by precomputing the full sequence or generating them dynamically during execution. In the dynamic approach, common in simple implementations, the outer loop iterates over the gaps as follows:for (int h = n / 2; h > 0; h /= 2) {
// Perform insertion sort on subsequences spaced by h
for (int i = h; i < n; i++) {
int temp = a[i];
int j;
for (j = i; j >= h && a[j - h] > temp; j -= h) {
a[j] = a[j - h];
}
a[j] = temp;
}
}
for (int h = n / 2; h > 0; h /= 2) {
// Perform insertion sort on subsequences spaced by h
for (int i = h; i < n; i++) {
int temp = a[i];
int j;
for (j = i; j >= h && a[j - h] > temp; j -= h) {
a[j] = a[j - h];
}
a[j] = temp;
}
}
Gap Sequences
Common Sequences
Shellsort's performance is highly dependent on the choice of gap sequence, with several well-established sequences developed over time to improve efficiency. The original sequence proposed by its inventor uses simple halvings, while subsequent refinements introduced geometric progressions and empirical optimizations. Donald Shell's original gap sequence, introduced in 1959, starts with an initial gap of and repeatedly halves it until reaching 1, yielding gaps such as .[1] This approach is straightforward but can lead to suboptimal performance, with a worst-case time complexity of .[2] In 1963, Thomas Hibbard proposed a sequence of gaps given by for , producing values like 1, 3, 7, 15, 31, and so on.[2] This sequence achieves a proven worst-case time complexity of , marking an early theoretical improvement over Shell's method.[2] Donald Knuth suggested a sequence in his seminal work on sorting algorithms, defined as for , generating gaps such as 1, 4, 13, 40, 121. This formulation balances the growth of gaps to reduce the number of inversions more evenly across passes, contributing to better practical behavior.[2] Incerpi and Sedgewick developed a hybrid sequence in 1985, combining elements of powers of 2 and 3 for empirical efficiency. It merges two geometric progressions—one with terms and the other (for )—resulting in gaps like 1, 5, 19, 41, 109, 209.[17] This sequence is noted for its fast average-case performance in practice due to its distribution of gap sizes.[18] Vaughan Pratt's sequence, outlined in his 1972 thesis, consists of all 3-smooth numbers (products of powers of 2 and 3, i.e., for non-negative integers ), arranged in decreasing order (largest first, down to 1) and truncated to those less than or equal to the array size; examples (in ascending order) include 1, 2, 3, 4, 6, 8, 9, 12.[19] It is theoretically significant for achieving worst-case complexity, though the sequence grows rapidly and is less practical for typical implementations.[19] A more recent empirical sequence by Marcin Ciura, published in 2001, was optimized through experimentation for array sizes up to . It uses fixed gaps: 1, 4, 10, 23, 57, 132, 301, 701, and so forth, selected to minimize average-case comparisons and swaps. This sequence outperforms many predecessors in benchmarks for moderate-sized inputs.[20]Sequence Properties and Design
Effective gap sequences in Shellsort are designed to balance efficiency across multiple insertion sort passes, with desirable properties including a geometric decrease in gap sizes—typically with ratios between 2 and 3—to ensure rapid reduction while maintaining coverage of the array elements.[2] Such ratios, like 11/5 ≈ 2.2, promote even distribution of comparisons and minimize the number of inversions carried over between passes.[2] Additionally, gaps should be pairwise relatively prime (i.e., coprime in pairs) to avoid amplifying periodic disorders in the input, and the overall sequence should aim for a small greatest common divisor, ideally 1, to prevent worst-case scenarios where subarrays remain poorly sorted. This even coverage ensures that distant elements are compared early, facilitating a smooth transition to finer sorting in later passes.[20] Designing these sequences involves key trade-offs, such as the number of passes versus the size of individual gaps: longer sequences with more smaller gaps increase the total work per pass but can lead to better overall sortedness, while shorter sequences with larger initial gaps reduce passes but risk inefficient final stages.[2] The goal is typically O(log n) passes to achieve sub-quadratic performance in practice, balancing computational overhead against the benefits of diminishing gap sizes.[2] For instance, sequences emphasizing fewer passes may use ratios closer to 3, trading some evenness for speed, whereas those prioritizing uniformity opt for slightly smaller ratios around 2.2. To avoid worst-case pathologies, sequences must minimize the potential for input-dependent degradations, such as when gaps share large common factors that align with adversarial patterns; employing coprime gaps and leveraging number-theoretic tools like the Frobenius coin problem helps bound the maximum disorder propagated, ensuring no subarray experiences excessive swaps.[2] This design choice is critical, as sequences with a greatest common divisor greater than 1 can trap elements in unsorted cycles, inflating the comparison count by up to a factor of the divisor. Early gap sequences, such as those proposed by Shell in 1959 and Hibbard in 1963, were derived theoretically based on intuitive progressions like multiples of 3 or powers of 2, aiming for simple asymptotic behavior without extensive testing.[2] In contrast, later developments by Incerpi and Sedgewick in the 1980s and Ciura in 2001 shifted toward empirical tuning through simulations on random permutations, optimizing for average-case metrics like comparisons and assignments on arrays up to several thousand elements.[20] These simulation-driven approaches revealed that theoretical sequences often underperform in practice due to high constants, favoring hybrid designs that incorporate both geometric growth and data-specific refinements.[2] Despite advances, the optimal gap sequence remains conjectural, with open problems centering on sequences that adapt to input distributions beyond uniform random data, potentially incorporating runtime analysis of disorder types.[2] Post-2000 research has explored parameterized functions for gaps; for example, a 2023 study introduced efficient sequences based on grid-search optimization of parameters, potentially improving scalability for larger inputs, though surpassing empirically tuned sequences like Ciura's remains challenging.[6] A 2021 analysis also examined gamma-parameterized gaps inspired by Ciura's method.[21] As an example evaluation, for an array of size n=1000, the theoretical Hibbard sequence (powers of 2 minus 1) requires approximately 10 passes, while the empirically tuned Ciura sequence uses about 8 passes, demonstrating how simulation-optimized designs reduce overhead without sacrificing effectiveness.[20]Analysis
Time Complexity
The time complexity of Shellsort depends heavily on the chosen gap sequence and input characteristics, with no tight universal bound established. In the worst case, it ranges from subquadratic to O(n^2) depending on the gaps, while a general lower bound of \Omega\left(n \left(\frac{\log n}{\log \log n}\right)^2\right) comparisons holds regardless of the sequence.[2][3] For specific gap sequences, performance improves with better designs. The original Shell sequence using powers of two yields a worst-case Θ(n^2), which performs worse than sequences like Hibbard's that achieve O(n^{3/2}). The Hibbard sequence (gaps of the form 2^k - 1) achieves a worst-case O(n^{3/2}). The Sedgewick sequence provides a worst-case O(n^{4/3}). However, the average-case complexity for practical sequences like Sedgewick's remains an open theoretical problem, though empirical studies indicate strong subquadratic performance.[2] A sketch of the derivation reveals why bounds vary. The algorithm performs a series of insertion sorts on disjoint subarrays for each gap h. With approximately n/h subarrays of length h, the worst-case cost per pass is O((n/h) \cdot h^2) = O(n h), as each insertion sort on a subarray of size h costs O(h^2). The total complexity is thus O\left(n \sum h_i\right) over all gaps h_i in the sequence; for geometrically decreasing gaps where \sum h_i = O(\sqrt{n}), this yields O(n^{3/2}). In the average case, inversion counting for random inputs reduces shifts, leading to O(n) per pass and subquadratic totals for good sequences.[2] For gap h, the n/h subarrays each require Θ(h^2) comparisons on average for random inputs, totaling Θ(n h) per pass. With good sequences and partial sorting from prior passes, the effective cost per pass reduces toward O(n), yielding subquadratic totals over O(log n) passes.[2] The best-case complexity is O(n \log n) for nearly sorted inputs, where minimal shifts occur per pass. Average-case performance for strong sequences approximates O(n^{1.3}), while worst-case inputs can be engineered for near-O(n^2) with adversarial gaps. Input randomness and sequence quality are key factors; empirical studies show Shellsort 2-5 times slower than quicksort on average but far faster than bubble sort for lists beyond 100 elements.[2][22][23]Space Complexity and Stability
Shellsort operates as a fully in-place algorithm, requiring only O(1) auxiliary space beyond the input array itself. This is achieved through the use of a constant number of variables, such as loop indices and a temporary variable for element swaps during comparisons.[24][25] The algorithm's implementation avoids recursion entirely and does not allocate extra arrays or data structures for intermediate results, distinguishing it from algorithms like merge sort that demand O(n) additional space for merging. This in-place nature makes Shellsort particularly advantageous in environments with limited memory availability, such as embedded systems or large datasets where auxiliary storage is prohibitive.[24] Regarding stability, Shellsort is not stable, meaning that equal elements may change their relative order in the sorted output compared to the input. Instability occurs primarily due to swaps in early passes with large gaps, where elements separated by the gap distance—potentially including equal keys—can be exchanged if their current positions violate the sorting order within their subarray. For instance, consider two identical elements positioned such that one precedes the other by the gap h; during the h-sort phase, the algorithm may shift them past each other to resolve inversions in interleaved subarrays.[26][27] While the insertion sort applied to each subarray during an h-sort phase preserves the relative order of equal elements locally within that subarray, the overall process interleaves multiple such subarrays, allowing global reordering of equals across different groups as gaps decrease. This contrasts with basic insertion sort, which is inherently stable because it only performs adjacent swaps and thus never inverts equal elements. Shellsort deliberately sacrifices this stability to enable faster corrections of distant inversions, enhancing its efficiency over insertion sort for larger, less ordered inputs.[24][27] To achieve stability when required, modified variants of Shellsort can incorporate stable sorting routines (such as a stability-preserving insertion sort) for each subarray phase, though this typically increases the time overhead due to additional checks for equal elements.[28]Applications and Comparisons
Practical Applications
Shellsort is particularly valued in embedded systems due to its in-place nature, which requires no additional memory allocation beyond the input array, and its avoidance of recursive calls that could lead to stack overflows in resource-constrained environments.[7] Its implementation simplicity further suits these platforms, enabling efficient handling of moderately sized datasets without the overhead of more complex algorithms.[29] In legacy software, Shellsort was formerly integrated into the Linux kernel's generic sorting routines to replace recursive qsort implementations, ensuring robustness in kernel-space operations with limited stack resources.[29] Similarly, the bzip2 compression utility utilizes Shellsort to sort block data, avoiding recursion depth issues that could disrupt compression in constrained environments like embedded file systems.[7] Shellsort is often incorporated into hybrid sorting strategies, such as combining it with quicksort for subarrays smaller than 50 elements, to mitigate quicksort's potential recursion depth problems while leveraging Shellsort's efficiency on small, nearly sorted inputs.[30] This approach enhances overall performance in general-purpose libraries by switching to Shellsort at low thresholds, reducing overhead from quicksort's partitioning on tiny segments.[30] Its non-recursive structure also aids in environments requiring bounded stack usage, such as task scheduling in real-time operating systems. Additionally, Shellsort serves as an educational tool for illustrating adaptive sorting concepts, demonstrating how incremental gap reductions improve upon basic insertion sort in introductory algorithms courses.[23] Shellsort continues to be used in the uClibc library for embedded systems. It also serves as a subroutine in hybrid algorithms like introsort for sorting small subarrays. In practice, Shellsort performs effectively for array sizes between 100 and 10,000 elements, where empirical benchmarks indicate it completes in approximately 10-20% of the time required by bubble sort on random data, owing to its sub-quadratic behavior.[23] Despite these strengths, Shellsort is rarely the first choice in modern applications, as optimized library implementations like introsort in standard C++ or Java libraries generally outperform it through hybrid techniques tailored for larger datasets.[30] Nonetheless, it remains valuable for understanding incremental algorithmic improvements and serves as a building block in custom sorting solutions for niche constraints.[7]Comparisons to Other Sorting Algorithms
Shellsort generalizes insertion sort by applying it to interleaved subarrays defined by decreasing gap sizes, enabling early swaps between distant elements that reduces the total number of inversions and improves the worst-case time complexity from O(n²) to sub-quadratic bounds, such as O(n^{4/3}) for certain gap sequences like Sedgewick's increments.[1][31] Compared to quicksort, Shellsort is fully in-place and non-recursive, avoiding pivot selection overhead and worst-case O(n²) behavior from poor partitions, though its average-case performance is typically 2–5 times slower due to higher constant factors in comparisons and swaps.[31] Empirical tests on random inputs show quicksort outperforming Shellsort variants for arrays larger than 10,000 elements; for example, on 75,000 integers, quicksort takes 20.8 seconds while a divisor-based Shellsort requires 32.4 seconds on comparable hardware (1987 benchmarks).[31] However, Shellsort offers more predictable runtime without randomization needs.| Array Size | Shellsort (Divisors) | Quicksort |
|---|---|---|
| 1,000 | 1.5 s | 1.7 s |
| 5,000 | 2.9 s | 2.6 s |
| 10,000 | 4.6 s | 3.8 s |
| 35,000 | 14.7 s | 10.5 s |
| 75,000 | 32.4 s | 20.8 s |
References
- Shellsort is a general-purpose sorting algorithm that was invented by Shell in 1959 [14]. Empirical results show that it is competitive with the fastest ...
- Simple sorting algorithm. – n-1 passes over the array. – At the end of pass i ... – invented by computer scientist Donald Shell in 1959. • based on some ...Missing: High- Speed Procedure
- A high-speed sorting procedure. In a recent note1, D. L. Shell has described a high-speed sorting procedure for lists contained in internal memory. · Sorting by ...
Summary of Shellsort from the Document
- This is an abstract of a survey talk on the theoretical and empirical studies that have been done over the past four decades on the Shellsort algorithm.

