Complete Java Shell Sort algorithm tutorial covering implementation with examples. Learn how to sort numeric and textual data in ascending and descending order.
Last modified: April 16, 2025
An algorithm is a step-by-step procedure to solve a problem or perform a computation. Sorting algorithms arrange elements of a list in a specific order, typically numerical or lexicographical. Efficient sorting is crucial for optimizing other algorithms.
Common sorting algorithms include Bubble Sort, Selection Sort, Insertion Sort, Shell Sort, Merge Sort, Quick Sort, and Heap Sort. Each has different time and space complexity characteristics making them suitable for different scenarios.
Shell Sort is an optimization of Insertion Sort that allows exchange of items far apart. It works by sorting elements at specific intervals, gradually reducing the interval until it becomes 1. The final pass is a regular Insertion Sort, but data is nearly sorted by then.
The algorithm’s time complexity depends on the gap sequence used. In the worst case it’s O(n²), but with optimal gaps it can reach O(n log² n). It’s an in-place algorithm but not stable (doesn’t maintain relative order of equal elements).
Here’s a basic implementation of Shell Sort in Java for sorting integers in ascending order. The gap sequence used is n/2, n/4, n/8, etc., which is simple but not optimal for performance.
ShellSort.java
package com.zetcode;
public class ShellSort {
public static void shellSort(int[] array) {
int n = array.length;
// Start with a big gap, then reduce the gap
for (int gap = n/2; gap > 0; gap /= 2) {
// Do a gapped insertion sort for this gap size
for (int i = gap; i < n; i++) {
int temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
public static void main(String[] args) {
int[] data = {12, 34, 54, 2, 3};
System.out.println("Array before sorting:");
for (int num : data) {
System.out.print(num + " ");
}
shellSort(data);
System.out.println("\nArray after sorting:");
for (int num : data) {
System.out.print(num + " ");
}
}
}
This implementation first divides the array into subarrays using the gap size. Each subarray is sorted using insertion sort. The gap is reduced until it becomes 1, at which point the array is nearly sorted.
Shell Sort can also sort strings lexicographically. Here’s an implementation that sorts an array of strings in ascending order. The comparison uses String’s compareTo method.
StringShellSort.java
package com.zetcode;
public class StringShellSort {
public static void shellSort(String[] array) {
int n = array.length;
for (int gap = n/2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
String temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap].compareTo(temp) > 0; j -= gap) {
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
public static void main(String[] args) {
String[] names = {"John", "Alice", "Bob", "Eve", "David"};
System.out.println("Names before sorting:");
for (String name : names) {
System.out.print(name + " ");
}
shellSort(names);
System.out.println("\nNames after sorting:");
for (String name : names) {
System.out.print(name + " ");
}
}
}
This version works similarly to the numeric sort but compares strings using compareTo. The algorithm maintains the same structure, only changing the comparison operation to work with strings instead of numbers.
To sort in descending order, we simply reverse the comparison condition. Here are both numeric and string versions modified for descending order.
DescendingShellSort.java
package com.zetcode;
public class DescendingShellSort {
// Numeric descending sort
public static void shellSortDesc(int[] array) {
int n = array.length;
for (int gap = n/2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap] < temp; j -= gap) {
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
// String descending sort
public static void shellSortDesc(String[] array) {
int n = array.length;
for (int gap = n/2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
String temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap].compareTo(temp) < 0; j -= gap) {
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
public static void main(String[] args) {
int[] numbers = {45, 23, 78, 12, 99, 34};
String[] words = {"apple", "orange", "banana", "grape", "kiwi"};
shellSortDesc(numbers);
shellSortDesc(words);
System.out.println("Numbers in descending order:");
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println("\nWords in descending order:");
for (String word : words) {
System.out.print(word + " ");
}
}
}
The only change needed for descending order is reversing the comparison operator (from > to < for numbers, and compareTo result from > 0 to < 0 for strings). This simple modification changes the sort direction.
Let’s compare Shell Sort with Quick Sort, one of the fastest general-purpose sorting algorithms. We’ll measure execution time for different array sizes to see their performance characteristics.
SortBenchmark.java
package com.zetcode;
import java.util.Random;
public class SortBenchmark {
public static void shellSort(int[] array) {
int n = array.length;
for (int gap = n/2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
public static void quickSort(int[] array, int low, int high) {
if (low < high) {
int pi = partition(array, low, high);
quickSort(array, low, pi - 1);
quickSort(array, pi + 1, high);
}
}
private static int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (array[j] < pivot) {
i++;
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
int temp = array[i + 1];
array[i + 1] = array[high];
array[high] = temp;
return i + 1;
}
public static void main(String[] args) {
int[] sizes = {1000, 10000, 100000, 1000000};
Random random = new Random();
for (int size : sizes) {
int[] data1 = new int[size];
int[] data2 = new int[size];
for (int i = 0; i < size; i++) {
int num = random.nextInt(size * 10);
data1[i] = num;
data2[i] = num;
}
// Shell Sort benchmark
long start = System.nanoTime();
shellSort(data1);
long shellTime = System.nanoTime() - start;
// Quick Sort benchmark
start = System.nanoTime();
quickSort(data2, 0, data2.length - 1);
long quickTime = System.nanoTime() - start;
System.out.printf("Size: %,d | Shell Sort: %,d ns | Quick Sort: %,d ns%n",
size, shellTime, quickTime);
}
}
}
This benchmark generates random arrays of different sizes and measures the time taken by each algorithm to sort them. Quick Sort generally outperforms Shell Sort, especially for larger datasets, due to its O(n log n) average time complexity compared to Shell Sort’s O(n log² n).
In this article, we’ve covered the Shell Sort algorithm in Java, including implementations for both numeric and textual data in ascending and descending order. We also compared its performance with Quick Sort through a benchmark.
My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.
List all Java tutorials.