# Shell Sort Handout

## What is shell sort?
- "Diminishing Increment Sort", better known as comb sort
- Combination of sorting by insertion (insertion sort) and sorting by exchange (bubble sort)
- Starts off by comparing two elements that are far apart and gradually reduce the gap between them
- It works better than regular insertion sort as it moves certain out of place elements into place faster

## Time Complexity (Shell's Original Sequence):
**Best Case:** $`O(NLogN)`$ <br/>
**Average Case:** $`O(N^\frac{5}{4})`$ <br/>
**Worst Case:** $`O(N^2)`$

## Space Complexity:
The space complexity of shell sort will always be __O(1)__ as it does not create another array

## Pros & Cons:
|Pros|Cons|
|:---|:---|
|- Efficient sorting algorithm for medium sized arrays <br/> - Good when memory restraint is tight (Merge and Quicksort will sometimes MLE) <br/> - The fastest non-recursive sorting algorithm <br/> - Much faster than insertion with basically the same amount of code <br/> - Easy to get working and only requires knowledge of iterations and conditional statements|- Heap, merge and quick sort are both more efficient <br/> - Not widely used as it is difficult to understand and implement <br/> - Many people often use the built in version ```Arrays.sort()``` which uses quick sort <br/> - Unstable sorting algorithm (Equal elements will appear in different keys) <br/> - Not as effective for extremely large data sets|

## Gap Sequences:
|General term $`(k \ge 1)`$|Concrete gaps|Worst-case time complexity|Author and year of publication|
|:------------------------:|:-----------:|:------------------------:|:----------------------------:|
|$`\Bigl\lfloor\dfrac{N}{2^K}\Bigr\rfloor`$|$`\dfrac{N}{2}`$, $`\dfrac{N}{4}`$, $`\dfrac{N}{2}`$|O(N^2)|Shell - 1959|
|$`2\Bigl\lfloor\dfrac{N}{2^{k+1}}\Bigr\rfloor+1`$|$`2\Bigl\lfloor\dfrac{N}{4}\Bigr\rfloor+1`$, $`3`$, $`1`$|$`O(N^\frac{3}{2})`$|Franz & Lazarus - 1960|
|$`2^k - 1`$|$`1, 3, 7, 15, 31, 63`$|$`O(N^\frac{3}{2})`$|Hibbard - 1963|
|$`2^k + 1`$|$`3, 5, 9, 17, 33, 65`$|$`O(N^\frac{3}{2})`$|Papernov & Stasevich - 1965|
|$`2^p`$$`3^q`$|$`1, 2, 3, 4, 6, 8, 9`$|$`O(NLog^{2}N)`$|Pratt - 1971|
|$`\dfrac{3^{k}-1}{2}`$|$`1, 4, 13, 40, 121`$|$`O(N^\frac{3}{2})`$|Pratt & Knuth - 1973|
|$`4^k+3\times2^{k-1}+1`$|$`1, 8, 23, 77, 281`$|$`O(N^\frac{4}{3})`$|Sedgewick - 1982|

## Sample Program
```java
public static void shellSort(int arr[]) // Method Header
    { 
        for(int gap = arr.length / 2; gap > 0; gap /= 2){ // Loop through the length of the gap sequences
            for(int i = gap; i < arr.length; i++){ // Loop through from the gap length to the length of the array
                int temp = arr[i], j; // Set the temp variable as the ith element in the array
                for(j = i; j >= gap && arr[j - gap] > temp; j -= gap){ // Loop through all the element to swap
                    arr[j] = arr[j - gap]; // Insert the jth element at the j - gap index
                }
                arr[j] = temp; // Insert temp at the jth element where the other element was swapped
            }
        }
    }
    
```

## Comparison to Other Sorting Algorithms:
### Shell vs Insertion
Shell sort can be seen as an improvement to insertion sort as it uses a variety of gap sequences and moves out of place elements into their correct positions faster whereas insertion sort will check each one by one. However, if the array is partially or nearly sorted, then insertion would still be the better option. Like selection sort, shell sort should be used for larger data sets.

### Shell vs Selection
Both insertion and shell sort can be used when the memory restraint is super tight and does not allow for auxillary memory. However, shell sort has a best case of $`O(NLogN)`$ while selection sort has a best time complexity of $`O(N^2)`$. For larger data sets, shell sort would obviously be more appealing than selection.

### Shell vs Quick
Shell sort and quick sort does not serve the same purpose, but many people tends to use quicksort as it is known as the "Queen of All Sorts" for its ability to sort randomized list in a short amount of time. However, shell sort is more favorable with strict space complexity as quick sort requires $`O(NLogN)`$ auxillary space complexity.

### Shell vs Merge
Merge sort is often regarded as one of the best sorting algorithms with consistent $`O(NLogN)`$ time complexity. However, like quick sort, it requires $`O(2N)`$ memory, which sometimes will result in MLE (Memory Limit Exceeded). For larger data sets, merge sort would, most of the time, be the better sorting algorithm.

## Enhanced Shell Sort
- A method to calculate the gap sequence and can decrease the number of comparisons by up to 60%
- Published by Basit Shahzad and Muhammad Tanvir Afzal from the World Academy of Science
- The gap sequence would be h<sub>1</sub> and h<sub>n+1</sub> = 3h<sub>n</sub>+1
- For 100 elements, shell's original gap seuqence will make 170 comparisons but the Enhanced Shell Sort will make 85 comparisons

## Testing
`10 Elements: 100, 37, 12, 86, 2, 127, 62, 14, 3, 9` <br/>
Shell Sort: *30* <br/>
Enhanced Shell Sort: *12*

`25 Elements: 100, 37, 12, 86, 2, 127, 62, 14, 3, 9, 30, 14, 90, 1, 20, 30, 74, 48, 37, 40, 7, 101, 200` <br/>
Shell Sort: *47* <br/>
Ehanced Shell Sort: *31*

`35 Elements: 1,2,23,12,25,7,9,5,36,1,100,37,12,86,2,127,62,14,3,9,30,14, 90,1,20,30,74,48,37,40,7,101,200,4,9 ` <br/>
Shell Sort: *202* <br/>
Enhanced Shell Sort: *105*

## Number of Comparisons to Array Size Chart
![alt text](https://i.imgur.com/SNfzwpU.png)
