 # Shell Sort in Data Structures

Amit Kumar Ghosh  18 min read
14 Jun 2023
Intermediate
407 Views

## Introduction

Are you interested in learning about different types of sorting algorithms? If so, then shell sort in data structure is worth looking into! Shell sort is an efficient and versatile algorithm used for sorting data structures like arrays. It has many advantages over other sorts and can be used to optimize the speed and order of simple to more complex program operations. In this article, we’ll explore how shell sort works, its benefits, as well as why it’s one of the best techniques for tackling tricky sorting problems in your data structure.

## What is Shell Sort in Data Structure

In the data structure, shell sort is a sorting algorithm that was invented by Donald Shell in 1959. The algorithm is an extension to the insertion sort method, where it sorts subsets of elements that are distant from each other rather than adjacent. This approach to sorting provides a faster execution time compared to other sorting algorithms such as bubble sort and insertion sort. The shell sort algorithm starts by dividing the data set into smaller segments, then sorting each of them using insertion sort. The algorithm then reduces the gap between the elements of the subset, and the sorting process is repeated until the entire data set is sorted. Shell sort in the data structure is a powerful and efficient sorting algorithm commonly used in practice due to its effectiveness in arranging complicated datasets.

## How Shell Sort works

1. The Shell Sort algorithm works by repeatedly sorting elements that are a certain distance apart and gradually reducing that distance until it becomes 1, at which point the algorithm performs a final insertion sort. Here's a step-by-step explanation of how Shell Sort works:
2. Choose a gap sequence: The first step in Shell Sort is to choose a sequence of gap values. The gaps determine the distance between the elements that will be compared and swapped during the sorting process. Common gap sequences include the Knuth sequence (3^k - 1) / 2 and the Sedgewick sequence (4^k + 3 * 2^(k-1) + 1).
3. Start with a large gap: Begin by comparing elements that are far apart. The gap value determines the initial distance between elements to be compared and swapped. The last gap value in the sequence is typically 1, which means the final step will be an insertion sort.
4. Compare and swap elements: Compare elements that are separated by the chosen gap. If the elements are out of order, swap them. This process is similar to the insertion sort but performed on a smaller subset of the array.
5. Reduce the gap: After sorting elements with the initial gap, reduce the gap size. The gap is typically reduced by dividing it by a constant factor (e.g., 2). Repeat the comparison and swapping process with the new gap size.
6. Repeat until the gap is 1: Continue reducing the gap and performing comparisons and swaps until the gap becomes 1. At this point, the algorithm performs a final insertion sort on the almost sorted array.
7. Final insertion sort: With a gap of 1, the algorithm behaves like a standard insertion sort. It compares adjacent elements and swaps them if necessary until the entire array is sorted

## Shell Sort Algorithm

``````shellSort(array, size)
for interval i <- size/2n down to 1
for each interval "i" in array
sort all the elements at interval "i"
end shellSort``````

## Implementation of Shell Sort

``````
# Shell sort in python

def shellSort(array, n):

# Rearrange elements at each n/2, n/4, n/8, ... intervals
interval = n // 2
while interval > 0:
for i in range(interval, n):
temp = array[i]
j = i
while j >= interval and array[j - interval] > temp:
array[j] = array[j - interval]
j -= interval

array[j] = temp
interval //= 2

data = [9, 8, 3, 7, 5, 6, 4, 1]
size = len(data)
shellSort(data, size)
print('Sorted Array in Ascending Order:')
print(data)
``````
``````
// Shell sort in Java programming

import java.util.Arrays;

// Shell sort
class ShellSort {

// Rearrange elements at each n/2, n/4, n/8, ... intervals
void shellSort(int array[], int n) {
for (int interval = n / 2; interval > 0; interval /= 2) {
for (int i = interval; i < n; i += 1) {
int temp = array[i];
int j;
for (j = i; j >= interval && array[j - interval] > temp; j -= interval) {
array[j] = array[j - interval];
}
array[j] = temp;
}
}
}

// Driver code
public static void main(String args[]) {
int[] data = { 9, 8, 3, 7, 5, 6, 4, 1 };
int size = data.length;
ShellSort ss = new ShellSort();
ss.shellSort(data, size);
System.out.println("Sorted Array in Ascending Order: ");
System.out.println(Arrays.toString(data));
}
}``````
``````
#include <iostream>
using namespace std;

// Shell sort
void shellSort(int array[], int n) {
// Rearrange elements at each n/2, n/4, n/8, ... intervals
for (int interval = n / 2; interval > 0; interval /= 2) {
for (int i = interval; i < n; i += 1) {
int temp = array[i];
int j;
for (j = i; j >= interval && array[j - interval] > temp; j -= interval) {
array[j] = array[j - interval];
}
array[j] = temp;
}
}
}

// Print an array
void printArray(int array[], int size) {
int i;
for (i = 0; i < size; i++)
cout << array[i] << " ";
cout << endl;
}

// Driver code
int main() {
int data[] = {9, 8, 3, 7, 5, 6, 4, 1};
int size = sizeof(data) / sizeof(data);
shellSort(data, size);
cout << "Sorted array: \n";
printArray(data, size);
}
``````

#### Output

``` ```Sorted array:
1 3 4 5 6 7 8 9``````

## Visualization of Shell Sort

Here's a visualization of the Shell sort algorithm:

Let's say we have an array of elements: [9, 4, 7, 2, 1, 5, 3, 8, 6]

Step 1: Initially, we choose a gap value (also known as the interval) for comparison and swapping. Let's start with a gap value of n/2 (where n is the number of elements in the array). In this case, n = 9, so the gap is 4.

Gap = 4 Step 2: Compare and swap elements that are at a gap of 4. Step 3: Reduce the gap value by dividing it by 2 (gap = gap/2).

Gap = 2 Step 4: Compare and swap elements that are at a gap of 2. Step 5: Reduce the gap value by dividing it by 2 (gap = gap/2).

Gap = 1 Step 6: Compare and swap elements that are at a gap of 1. Step 7: Since the gap is 1, the array is sorted.

The steps above demonstrate the intermediate stages of the Shell sort algorithm. The algorithm continues to reduce the gap value until it becomes 1, at which point it behaves like an insertion sort. The final sorted array is obtained at the end of the process.

## Shell Sort Complexity

• The time complexity of Shell Sort depends on the gap sequence used for sorting. A gap sequence defines the gap between elements that are compared at each iteration. The original gap sequence proposed by Shell was based on the powers of two, but various other gap sequences have been suggested over the years.
• The worst-case time complexity of Shell Sort with the original Shell gap sequence is O(n^2), where n is the number of elements to be sorted. However, many other gap sequences can achieve better performance.
• One commonly used gap sequence is the Knuth sequence, which is calculated as (3^k - 1) / 2, where k is an incrementing index starting from 0. With the Knuth sequence, the time complexity of Shell Sort is O(n^(3/2)) in the worst case.
• There are other gap sequences, such as the Sedgewick sequence, that can further improve the time complexity. With the Sedgewick sequence, the time complexity of Shell Sort is approximately O(n^(4/3)) in the worst case.
• It's important to note that the time complexity of Shell Sort heavily depends on the gap sequence used. Different gap sequences have different performance characteristics, and choosing an efficient gap sequence is crucial for achieving better time complexity.
• Overall, while the worst-case time complexity of Shell Sort is typically worse than more advanced sorting algorithms like Merge Sort or Quick Sort, it can still provide reasonable performance in practice for small-to-medium-sized arrays or partially sorted arrays.
 Time Complexity Best O(nlog n) Worst O(n2) Average O(nlog n) Space Complexity O(1) Stability No

## Shell Sort Applications

Shell sort can be applied to sort various types of data, including:

1. Numeric data: Shell sort works well for sorting arrays or lists of numeric data, such as integers or floating-point numbers. It can handle both small and large data sets efficiently.
2. Strings: Shell sort can also be applied to sort strings based on alphabetical order or other defined criteria. It can arrange a list of names or words in a desired order.
3. Records or objects: If you have a collection of records or objects that need to be sorted based on one or more attributes, Shell sort can be used. You can define custom comparison functions or operators to determine the sorting order based on specific attributes.
4. Large data sets: Shell sort is often used in situations where the data set is large and other sorting algorithms may be too slow. It provides better performance compared to simple algorithms like bubble sort or insertion sort.
5. Online sorting: Shell sort can also be applied in situations where new elements are continuously added to an already sorted list. It can efficiently handle incremental sorting by adapting the gap sequence dynamically as new elements are inserted

• Efficient for medium-sized lists: Shell sort performs well for lists of medium size. It improves upon insertion sort, which is efficient for small lists but becomes less effective as the list size grows. By using the concept of diminishing increment, Shell sort can provide better performance than insertion sort for medium-sized lists.
• In-place sorting: Shell sort is an in-place sorting algorithm, meaning it doesn't require additional memory space proportional to the input size. It operates directly on the input list, making it memory efficient and suitable for situations with limited memory availability.
• Adaptive nature: Shell sort is an adaptive algorithm, which means it can take advantage of partially sorted lists. It starts with a large gap between elements and gradually reduces the gap until it becomes 1 (which is essentially insertion sort). This adaptive nature allows Shell sort to handle partially sorted or nearly sorted lists more efficiently than other sorting algorithms.
• Time complexity: While the worst-case time complexity of Shell sort is generally considered to be O(n^2), where n is the number of elements in the list, the average-case and best-case time complexities can be better. The time complexity depends on the chosen gap sequence. With a good gap sequence, Shell sort can achieve a time complexity of O(n log n) or even better.
• Simple implementation: Shell sort is relatively easier to implement compared to more complex sorting algorithms like quicksort or mergesort. Its basic idea revolves around the concept of comparing and swapping elements with a certain gap. This simplicity makes it a popular choice in situations where a quick and easy-to-implement sorting algorithm is required.
• Wide range of applications: Shell sort finds its applications in various scenarios. It is commonly used in embedded systems and applications where memory is limited. Additionally, it can be useful in scenarios where a quick and efficient sorting algorithm is required but the input size is not excessively large.

• Complexity analysis: The time complexity of shell sort is difficult to analyze precisely, as it depends on the chosen gap sequence. While the average case time complexity is considered to be better than the quadratic time complexity of simple insertion sort, it is still worse than many other sorting algorithms like quicksort or merge sort.
• Unstable sort: Shell sort is generally an unstable sorting algorithm, meaning that it may change the relative order of equal elements. If the stability of sorting is a requirement, other algorithms like merge sort or insertion sort can be preferred.
• Non-optimal gap sequence selection: The performance of shell sort heavily relies on the choice of gap sequence. Different gap sequences can lead to significantly different running times for the same input. While some gap sequences, such as the original Shell sequence or the Knuth sequence, perform well in practice, finding the optimal gap sequence for a given input is a challenging task.
• Implementation complexity: The implementation of shell sort can be more complex than other simpler sorting algorithms. The algorithm requires determining the gap sequence and managing the iterations with varying gaps. This complexity can make the code harder to understand and debug.
• Limited practical use: In practice, shell sort is not commonly used as a primary sorting algorithm. More efficient and easier-to-implement algorithms like quicksort, mergesort, or heapsort are often preferred. These algorithms have better average and worst-case time complexities, making them more suitable for general-purpose sorting.
##### Summary

In conclusion, shell sort has proven to be a great method when it comes to sorting data. It is especially advantageous because its runtime complexity is relatively low compared to other algorithms, and it remains numerically stable even in cases of large data sets. Despite these positive qualities, it does come with some drawbacks which make it the less appealing option when compared to other methods; this includes its large memory requirement and the lack of effectiveness when confronted with certain types of data. That's why it's important for developers to have an understanding of different sorting algorithms when building applications so that they can determine which one best suits their needs. If you're looking for an algorithm that performs quickly while also offering numerical stability, then you may consider making use of shell sort in your next project.

Similar Articles 