AlgoMaster Logo

Shell Sort

Last Updated: March 31, 2026

Ashish

Ashish Pratap Singh

Shell Sort is an optimization of Insertion Sort designed to handle larger inputs more efficiently. Instead of comparing adjacent elements right away, it starts by comparing elements that are far apart, gradually reducing the gap between them. This allows the algorithm to move elements closer to their correct positions early on.

By the time the gap becomes 1, the array is already partially sorted, making the final pass much faster than a regular Insertion Sort. This simple idea significantly improves performance in practice.

In this chapter, you will learn how Shell Sort works, how the gap sequence affects its performance, and how to implement it. You will also understand why it performs better than basic quadratic sorting algorithms in many real-world scenarios.

What Is Shell Sort?

Shell sort is a generalization of insertion sort that allows the exchange of elements that are far apart. Instead of comparing adjacent elements, it compares elements separated by a "gap" and uses insertion sort on each group of elements spaced by that gap. Over successive passes, the gap shrinks until it reaches 1, at which point the algorithm performs a standard insertion sort on an array that is already nearly sorted.

The key idea is this: by sorting elements that are far apart early on, we eliminate large amounts of disorder in the array quickly. Each pass with a smaller gap brings the array closer to sorted order. By the time the gap reaches 1, most elements are already close to their final positions, so the last pass runs in nearly O(n) time.

Here is how Shell sort compares elements at different gap values versus regular insertion sort:

In insertion sort, each element can only compare with its immediate neighbor. Shell sort breaks this restriction by comparing elements separated by a gap. Elements in the same gap-group get sorted together, so large displacements are resolved early.

Why this matters: Insertion sort runs in O(n^2) because elements can only move one position per comparison. If an element needs to travel n positions, it takes n swaps. Shell sort lets elements jump across large distances in a single step, dramatically reducing the total number of operations.

How Shell Sort Works

The algorithm follows a simple three-level loop structure:

  1. Choose a starting gap (typically n/2, where n is the array size)
  2. Perform a gapped insertion sort for the current gap value
  3. Reduce the gap (typically by half) and repeat until gap = 1

When the gap is larger than 1, the algorithm groups elements that are gap positions apart and sorts each group using insertion sort. As the gap decreases, the groups overlap more and more. By the time the gap reaches 1, we are doing a standard insertion sort, but on an array that is already nearly sorted.

Gap Sequences

The choice of gap sequence has a significant impact on performance. Different sequences yield different time complexities:

Gap SequenceFormulaExample (n=16)Worst-Case Time
Shell's originaln/2, n/4, ..., 18, 4, 2, 1O(n^2)
Knuth's(3^k - 1) / 21, 4, 13, 40, 121, ...O(n^(3/2))
Hibbard's2^k - 11, 3, 7, 15, 31, ...O(n^(3/2))
Sedgewick's4^k + 3*2^(k-1) + 11, 5, 19, 41, 109, ...O(n^(4/3))
Tokuda'sceil((9*(9/4)^k - 4) / 5)1, 4, 9, 20, 46, ...Unknown (empirically fast)

Shell's original sequence (n/2) is the simplest and most commonly taught, but it is not the most efficient. Knuth's and Sedgewick's sequences perform better in practice because they avoid the issue of "increment interaction," where certain gap values fail to compare elements that were already compared in previous passes.

For interviews and most practical purposes, Shell's original sequence (dividing by 2 each time) is perfectly acceptable. Just be aware that better sequences exist.

The Algorithm Step by Step

The outer loop controls the gap, the middle loop iterates through elements starting from index gap, and the inner loop performs the gapped insertion sort. This inner loop is identical to insertion sort, except instead of comparing with the previous element (j-1), it compares with the element gap positions back (j-gap).

When the gap finally reaches 1, the algorithm performs one last pass of standard insertion sort. But because the earlier passes have already moved elements close to their final positions, this last pass does very little work, typically running in close to O(n) time.

Code Implementation

The algorithm itself is short, with no recursion and no auxiliary data structures. This simplicity is one of Shell sort's biggest advantages.

Example Walkthrough

Let us trace through Shell sort on the array [12, 34, 54, 2, 3] using Shell's original gap sequence (n/2).

Initial array: [12, 34, 54, 2, 3], n = 5

Pass 1: gap = 2

With gap = 2, we are sorting elements that are 2 positions apart. This creates the following interleaved sub-arrays:

  • Sub-array 1 (indices 0, 2, 4): [12, 54, 3]
  • Sub-array 2 (indices 1, 3): [34, 2]

Now we walk through each element starting from index gap = 2:

i = 2: temp = 54, j = 2

  • Compare arr[0] = 12 with temp = 54. Since 12 < 54, no shift needed.
  • arr[2] = 54 (unchanged)
  • Array: [12, 34, 54, 2, 3]

i = 3: temp = 2, j = 3

  • Compare arr[1] = 34 with temp = 2. Since 34 > 2, shift: arr[3] = 34, j = 1.
  • j = 1 < gap = 2, so stop.
  • arr[1] = 2
  • Array: [12, 2, 54, 34, 3]

i = 4: temp = 3, j = 4

  • Compare arr[2] = 54 with temp = 3. Since 54 > 3, shift: arr[4] = 54, j = 2.
  • Compare arr[0] = 12 with temp = 3. Since 12 > 3, shift: arr[2] = 12, j = 0.
  • j = 0 < gap = 2, so stop.
  • arr[0] = 3
  • Array: [3, 2, 12, 34, 54]

After gap = 2, the sub-arrays are sorted:

  • Sub-array 1 (indices 0, 2, 4): [3, 12, 54] (was [12, 54, 3])
  • Sub-array 2 (indices 1, 3): [2, 34] (was [34, 2])

The array [3, 2, 12, 34, 54] is already much more ordered than the original. Large elements moved to the right, small elements moved to the left.

Pass 2: gap = 1

This is standard insertion sort, but on a nearly sorted array.

i = 1: temp = 2, j = 1

  • Compare arr[0] = 3 with temp = 2. Since 3 > 2, shift: arr[1] = 3, j = 0.
  • arr[0] = 2
  • Array: [2, 3, 12, 34, 54]

i = 2: temp = 12, j = 2

  • Compare arr[1] = 3 with temp = 12. Since 3 < 12, no shift.
  • Array: [2, 3, 12, 34, 54]

i = 3: temp = 34, j = 3

  • Compare arr[2] = 12 with temp = 34. Since 12 < 34, no shift.
  • Array: [2, 3, 12, 34, 54]

i = 4: temp = 54, j = 4

  • Compare arr[3] = 34 with temp = 54. Since 34 < 54, no shift.
  • Array: [2, 3, 12, 34, 54]

Final sorted array: [2, 3, 12, 34, 54]

Notice how the gap = 1 pass only needed one swap (moving 2 before 3). The heavy lifting was done in the gap = 2 pass. This is exactly why Shell sort is faster than plain insertion sort: the final pass operates on an almost-sorted array.

Complexity Analysis

Shell sort's complexity is unique among sorting algorithms because it depends heavily on the gap sequence, not just the input.

Time Complexity

Gap SequenceWorst CaseAverage CaseBest Case
Shell's (n/2)O(n^2)O(n^(3/2))O(n log n)
Knuth's (3h+1)O(n^(3/2))O(n^(7/6))O(n log n)
Hibbard's (2^k - 1)O(n^(3/2))O(n^(5/4))O(n log n)
Sedgewick'sO(n^(4/3))O(n^(7/6))O(n log n)

Best case (O(n log n)): When the array is already sorted, each gap pass makes one comparison per element without any shifts. Since there are O(log n) gap values, the total is O(n log n).

Worst case with Shell's sequence (O(n^2)): This happens when the gap sequence causes redundant comparisons. For example, with gaps 8, 4, 2, 1, elements at odd indices are never compared with elements at even indices until the very last pass, which then degenerates into a slow insertion sort.

Why better sequences help: Knuth's and Sedgewick's sequences are designed so that consecutive gap values share fewer common factors. This ensures that each pass compares elements that previous passes missed, leading to better performance.

Space Complexity

Shell sort uses O(1) auxiliary space. It sorts in-place, using only a single temporary variable for the insertion sort swap. This makes it one of the most memory-efficient sorting algorithms.

Stability

Shell sort is not stable. Equal elements may change their relative order during gapped passes. Consider two equal elements at positions 0 and 3 with gap = 2. They belong to different sub-arrays and might be rearranged relative to each other.

Here is a summary comparing Shell sort with related algorithms:

PropertyInsertion SortShell SortMerge SortQuick Sort
Best CaseO(n)O(n log n)O(n log n)O(n log n)
Average CaseO(n^2)O(n^(3/2))*O(n log n)O(n log n)
Worst CaseO(n^2)O(n^(3/2))*O(n log n)O(n^2)
SpaceO(1)O(1)O(n)O(log n)
StableYesNoYesNo
In-PlaceYesYesNoYes
RecursiveNoNoYesYes

*With Knuth's gap sequence. Shell's original sequence gives O(n^2) worst case.

When to Use Shell Sort

Good Use Cases

  • Medium-sized arrays (hundreds to low thousands of elements): Shell sort outperforms insertion sort significantly and has lower overhead than merge sort or quick sort for moderate input sizes.
  • Embedded systems and constrained environments: No recursion means no risk of stack overflow, and O(1) space means no extra memory allocation. This makes Shell sort a solid choice for microcontrollers and systems with limited resources.
  • When insertion sort is too slow but you want simplicity: Shell sort's code is barely more complex than insertion sort. If you need a quick performance boost without implementing a full divide-and-conquer algorithm, Shell sort is the natural next step.
  • Nearly sorted data: Like insertion sort, Shell sort performs well on data that is already partially ordered. The initial passes have little work to do, and the final pass is nearly O(n).
  • As a component in hybrid sorting algorithms: Some implementations of introspective sort use Shell sort for small partitions instead of insertion sort because it handles slightly larger sub-arrays more efficiently.

Poor Use Cases

  • Very large datasets (millions of elements): For large inputs, O(n log n) algorithms like merge sort, quick sort, or heap sort are significantly faster. Shell sort cannot compete at that scale.
  • When stability is required: If you need equal elements to maintain their original order, Shell sort is not the right choice. Use merge sort or Timsort instead.
  • When worst-case guarantees matter: Shell sort's worst case depends on the gap sequence and is hard to pin down precisely. If you need guaranteed O(n log n) performance, use merge sort or heap sort.
  • Linked lists: Shell sort relies on random access to elements at arbitrary gap distances. Linked lists do not support efficient random access, making Shell sort impractical for them.