ShellSort is mainly a variation of Insertion Sort.
In insertion sort, we move elements only one position ahead. When an element has to be moved far ahead, many movements are involved.
The idea of shellSort is to allow exchange of far items.
int shellSort(int arr[], int n)
{
// 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 = arr[i];
for (int j = i; j >= gap && arr[j - gap] > temp; j -= gap)
arr[j] = arr[j - gap];
arr[j] = temp;
}
}
}
Some sorting algorithms are stable by nature like Insertion sort, Merge Sort, Bubble Sort, etc.
And some sorting algorithms are not, like Heap Sort, Quick Sort, etc.
Category Archives: Sorting
Radix sort
The idea of Radix Sort is to do digit by digit sort starting from least significant digit to most significant digit.
Let there be d digits in input integers. Radix Sort takes O(d*(n+b)) time where b is the base for representing numbers,
for example, for decimal system, b is 10.
If k is the maximum possible value, then d would be O(Logb(k)). So overall time complexity is O((n + b) * Logb(k)) .
If we set b as n, we get the time complexity as O(n).
In other words, we can sort an array of integers with range from 1 to nc if the numbers are represented in base n (or every digit takes bits).
void radixsort(int *a, int n)
{
int i, b[MAX], m = a[0], exp = 1;
//Find the greatest value
for (i = 1; i < n; i++)
{
if (a[i] > m)
m = a[i];
}
//Loop until exp is bigger than the largest number
while (m / exp > 0)
{
int bucket[BASE] = { 0 };
for (i = 0; i < n; i++)
bucket[(a[i] / exp) % BASE]++;
//Add the count of the previous buckets to acquire the indexes after the
end of each bucket location in the array
for (i = 1; i < BASE; i++)
bucket[i] += bucket[i - 1];
for (i = n - 1; i >= 0; i--)
b[--bucket[(a[i] / exp) % BASE]] = a[i];
for (i = 0; i < n; i++)
a[i] = b[i];
//Multiply exp by the BASE to get the next group of keys
exp *= BASE;
}
}
Bucket Sort
Bucket sort is mainly useful when input is uniformly distributed over a range.
Void Bucket_Sort(int X[n], int n)
{
int i,j, Y[n+1];
for (i = 0; i < n; i++) Y[i] = 0;
for (i = 0; i < n; i++) Y[X[i]]++;
for (i = 0, j = 0; i < n; i++)
while(Y[i]-- > 0) X[j++] = i
}
Runtime – O(n)
Quick Sort using Linked list
struct node
{
int data;
struct node *link;
struct node *plink;
};
struct node *first=NULL, *last=NULL;
void swap(struct node* a,struct node* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void qsort(struct node *low, struct node *high)
{
if(low==NULL || high==NULL || low == high || low->plink == high)
return ;
struct node *pivot=low, *tmp=low->link;
while(tmp != high->link)
{
if(tmp->data < low->data)
{
swap(pivot->link, tmp);
pivot = pivot->link;
}
tmp = tmp->link;
}
swap(low, pivot);
qsort(low, pivot->plink);
qsort(pivot->link, high);
}
Quicksort can be implemented for Linked List only when we can pick a fixed point as pivot. Random QuickSort cannot be efficiently implemented for Linked Lists.
Quick Sort using Double Linked list
struct node
{
int data;
struct node *next;
struct node *prev;
};
void swap(struct node* a,struct node* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void qsort(struct node *low, struct node *high)
{
if(low==NULL || high==NULL || low == high || low->next == high)
return ;
struct node *pivot=low, *tmp=low->next;
while(tmp != high-next)
{
if(tmp->data < low->data)
{
swap(pivot->next, tmp);
pivot = pivot->next;
}
tmp = tmp->next;
}
swap(low, pivot);
qsort(low, pivot->previous);
qsort(pivot->next, high);
}
Quick Sort
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
void QuickSort(int *array, int l, int h)
{
if(l >= h)return;
int pivot = array[l]; /*Pivot is the starting element */
int i = l, temp;
for(int j = l + 1;j <= h;j++)
{
if(array[j] < pivot)
{
i++;
swap(&array[i], &array[j]);
}
}
/*
_______________________i____________________
|pivot|....<=pivot......|....>=pivot......|
*/
swap(&array[i], &array[l]);
/*
____________________i______________________
|...<=pivot......|pivot|....>=pivot......|
*/
QuickSort(array, l, i-1);
QuickSort(array, i+1, h);
}
Although the worst case time complexity of QuickSort is O(n2)
which is more than many other sorting algorithms like Merge Sort and Heap Sort,
QuickSort is faster in practice, because its inner loop can be efficiently implemented on most architectures,
and in most real-world data. quicksort's sequential and localized memory references work well with a cache
Best Case and Average Case: O(nLogn)
QUICK SORT ITERATIVE
int partition (int arr[], int l, int h)
{
int x = arr[h];
int i = l;
for (int j = l+1; j <= h; j++)
{
if (arr[j] <= x)
{
i++;
swap (&arr[i], &arr[j]);
}
}
swap (&arr[i], &arr[h]);
return (i);
}
void quickSortIterative (int arr[], int l, int h)
{
int stack[ h - l + 1 ];
int top = -1;
stack[ ++top ] = l;
stack[ ++top ] = h;
while ( top >= 0 )
{
h = stack[ top-- ];
l = stack[ top-- ];
int p = partition( arr, l, h );
if ( p-1 > l ) {
stack[ ++top ] = l;
stack[ ++top ] = p - 1;
}
if ( p+1 < h ){
stack[ ++top ] = p + 1;
stack[ ++top ] = h;
}
}
}
Merge sort using Linked List
struct node
{
int data;
struct node* next;
};
void MergeSort(struct node** headRef)
{
struct node* head = *headRef;
struct node* a;
struct node* b;
if ((head == NULL) || (head->next == NULL)){
return;
}
FrontBackSplit(head, &a, &b);
MergeSort(&a);
MergeSort(&b);
*headRef = SortedMerge(a, b);
}
struct node* SortedMerge(struct node* a, struct node* b)
{
struct node* result = NULL;
if (a == NULL)
return(b);
else if (b==NULL)
return(a);
if (a->data <= b->data){
result = a;
result->next = SortedMerge(a->next, b);
}
else {
result = b;
result->next = SortedMerge(a, b->next);
}
return(result);
}
/* Split the nodes of the given list into front and back halves.
If the length is odd, the extra node should go in the front list. */
void FrontBackSplit(struct node* source, struct node** frontRef,
struct node** backRef){
struct node* fast;
struct node* slow;
if (source==NULL || source->next==NULL) {
*frontRef = source;
*backRef = NULL;
}
else {
slow = source;
fast = source->next;
/* Advance 'fast' two nodes, and advance 'slow' one node */
while (fast != NULL)
{
fast = fast->next;
if (fast != NULL)
{
slow = slow->next;
fast = fast->next;
}
}
/* 'slow' is before the midpoint in the list, so split it in two
at that point. */
*frontRef = source;
*backRef = slow->next;
slow->next = NULL;
}
}
Merge Sort
int *temp = (int *)malloc(sizeof(int)*array_size);
int mergeSort(int arr[], int temp[], int left, int right)
{
if (right > left)
{
int mid = (right + left)/2;
mergeSort(arr, temp, left, mid);
mergeSort(arr, temp, mid+1, right);
merge(arr, temp, left, mid, right);
}
}
int merge(int arr[], int temp[], int left, int mid, int right)
{
int i, j, k;
i = left;
j = mid+1;
k = left;
while ((i <= mid) && (j <= right))
{
if (arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
{
temp[k++] = arr[j++];
}
}
while (i <= mid)
temp[k++] = arr[i++];
while (j <= right)
temp[k++] = arr[j++];
for (i=left; i <= right; i++)
arr[i] = temp[i];
}
Time Complexity: O(nLog(n))
Heap Sort
void swap(int *a, int *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
void minHeapify(int a[], int size, int i)
{
int l = 2*i + 1; //using zero based arrays
int r = 2*i + 2;
int smallest = i;
if(l<size && a[l]<a[smallest])
smallest = l;
if(r<size && a[r]<a[smallest])
smallest = r;
if(smallest!=i)
{
swap(&a[i],&a[smallest]);
minHeapify(a,size,smallest);
}
}
void buildMinHeap(int a[], int size) {
for(int i=size/2;i>=0;i--)
minHeapify(a,size,i);
}
void heapsort (int a[], int size)
{
buildMinHeap(a,size);
while (size > 1)
{
swap(&a[0], &ma[size - 1]);
-- size;
minHeapify(a,size,0);
}
}
void removeMin(int a[], int size)
{
if (isEmpty())
throw new HeapException("Heap is empty");
else {
a[0] = a[size - 1];
size--;
if (heapSize > 0)
minHeapify(a,size,0);
}
}
Insertion Sort
void insertionSort(int arr[], int n)
{
int i, key, j;
for (i = 1; i < n; i++)
{
key = arr[i];
j = i-1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key)
{
arr[j+1] = arr[j];
j = j-1;
}
arr[j+1] = key;
}
}
Time Complexity: O(n*n)
Uses: Insertion sort is uses when number of elements is small. It can also be useful when
input array is almost sorted, only few elements are misplaced in complete big array.
Bubble sort
void bubbleSort(int arr[], int n)
{
int i, j;
bool swapped;
for (i = 0; i < n; i++)
{
swapped = false;
for (j = 0; j < n-i-1; j++)
{
if (arr[j] > arr[j+1])
{
swap(&arr[j], &arr[j+1]);
swapped = true;
}
}
// IF no two elements were swapped by inner loop, then break
if (swapped == false)
break;
}
}
void swap(int *xp, int *yp)
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
Time Complexity: O(n*n)