I study Data Structure and Algorithm
in GeeksforGeeks and play with the algorithms in C. In this part, I will cover algorithms of searching and sorting!
Header file
Since that some methods are used quite often, I write a header file for this part.
/* File name: util.h */
#include <stdio.h>
/* Function to swap two values */
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
/* Function to print an array */
void printArray(int arr[], int size)
{
int i;
for (i=0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
Binary search
This algorithm supposes that an array is already sorted and search the sub-array in dependent of the middle number and our search number. Its time complexity T(n)=O(log[n]) .
#include <stdio.h>
// Binary search for half part recursively
int binary_search(int arr[], int l, int r, int x){
if (r >= l){
int middle = l + (r-1)/2;
if ( arr[middle] == x) return middle;
if ( arr[middle] < x) return binary_search(arr, middle+1, r, x);
return binary_search(arr, l, middle-1, x);
}
return -1;
}
int main(void){
int arr[] = {1, 2, 4, 5, 6, 9};
int n = sizeof(arr)/ sizeof(arr[0]);
int x = 9;
int result = binary_search(arr, 0, n-1, x);
(result == -1)? printf("Not Found!\n")
: printf("Element is presented at index %d\n", result );
return 0;
}
Selection Search
This algorithm finds the smallest element repeatedly in the unsorted part and stacks it at the beginning. Its time complexity is T(n)=O(n2) . I use recurrence in this case, but you can use two nested loops, either. It is quite the same.
#include <stdio.h>
void selectionsort(int arr[], int l, int r){
if (r >= l){
int flag = l;
int i;
// Find the smallest index 'flag'
for (i = l + 1; i <= r; i++){
if (arr[flag] > arr[i]) flag = i;
}
// Swap arr[flag] and arr[l]
int temp = arr[flag];
arr[flag] = arr[l];
arr[l] = temp;
selectionsort(arr, l+1, r);
}
}
int main(void){
int j ;
int arr[] = {1, 5, 3, 2, 9, 7, 6};
int n = sizeof(arr)/ sizeof(arr[0]);
selectionsort(arr, 0, n-1);
for (j = 0; j < n; j++)
printf("%d\n", arr[j] );
}
Note: Selection sort makes O(n) swaps which is minimum among all sorting algorithms mentioned above.
Bubble sort
Bubble sort is the simplest sorting algorithm. It works by continuously comparing the element with the unsorted sub-part. In the first iteration, it results in a largest or smallest element to be placed in the end or the beginning of an array respectively. Such method is continue on with the unsorted array. Time complexity: T(n)=O(n2) .
#include <stdio.h>
#include "util.h"
void bubbleSort(int arr[], int n){
int i, j;
for (i = 0; i < n -1; i++){
for (j = 0; j < n - i -1; j++){
if (arr[j] > arr[j+1]) swap(&arr[j], &arr[j+1]);
}
}
}
// Driver program to test above functions
int main()
{
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr)/sizeof(arr[0]);
bubbleSort(arr, n);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
Insertion sort
Insertion runs by insert an element (from i=1 to i=n ) to the listed left array. Its time complexity is O(n2) .
#include <stdio.h>
#include "util.h"
void insertion_sort(int arr[], int l, int r){
if( l < r){
for(int i = 0; i < l; i++){
/* If arr[i] is larger than the left sorted part*/
if (arr[i] > arr[l]){
for(int j = l; j > i; j--) swap(&arr[j], &arr[j-1]);
}
}
insertion_sort(arr, l+1, r);
}
}
int main(void){
int arr[] = {1,34,5,21,43,28,17};
int n = sizeof(arr)/ sizeof(arr[0]);
insertion_sort(arr, 0, n);
printArray(arr, n);
return 0;
}
The algorithm can be optimized by using binary search to find out the from which element that the sub-array is greater than the i−th element from i−th iteration.
#include <stdio.h>
#include "util.h"
void insertion_sort(int arr[], int length){
int temp, low, mid, high;
for (int i = 1; i < length; i++){
/* sorted subarray: 0 ~ i-1 */
low = 0;
temp = arr[i];
high = i - 1;
/* Binary search */
while (low <= high){
mid = (low + high)/ 2;
/* If arr[i] is smaller, its location should be on the left of mid */
if( arr[mid] > temp) high = mid - 1;
/* If arr[i] is larger, its location should be on the right of mid */
else low = mid + 1;
}
for (int j = i ; j > high; j--)
arr[j] = arr[j-1];
arr[high+1] = temp;
}
}
int main(void){
int arr[] = {1,34,5,21,43,28,17,2,9,32};
int n = sizeof(arr)/ sizeof(arr[0]);
insertion_sort(arr, n);
printArray(arr, n);
return 0;
}
Merge sort
Merge sort is a divide and conquer algorithm, it cuts an array into two halves, then sort them and merge them together. Its time complexity is O(nlogn) in 3 cases(worst, best, average) .
#include <stdio.h>
#include "util.h"
void merge(int arr[], int l, int m, int r){
int n1, n2, i, j;
/* number of subaray */
n1 = m - l + 1;
n2 = r - m;
int L[n1], R[n2];
/* Copy two subarrays */
for (i = 0; i < n1; i++){
L[i] = arr[l + i];
}
for (j = 0; j < n2; j++){
R[j] = arr[m + j + 1];
}
/* i: index of L[], j: index of R[], k: index of arr[] */
i = 0; j = 0;
int k = l;
while (i < n1 && j < n2){
if (L[i] <= R[j]){
arr[k] = L[i];
k++; i++;
}
else {
arr[k] = R[j];
k++; j++;
}
}
while (i < n1){
arr[k] = L[i];
k++; i++;
}
while (j < n2){
arr[k] = R[j];
k++; j++;
}
}
void merge_sort(int arr[], int l, int r){
if (l < r){
/* Equal to (l+r)/2 */
int mid = l + (r-l)/ 2;
merge_sort(arr, l, mid);
merge_sort(arr, mid+1, r);
merge(arr, l, mid, r);
}
}
int main(void){
int arr[] = {1,34,5,21,43,28,17,12};
int n = sizeof(arr)/ sizeof(arr[0]);
merge_sort(arr, 0, n - 1);
printArray(arr, n);
return 0;
}
Quick sort
Quick is an algorithm being used widely in actual application. It starts by choose a pivot and place those smaller elements on the left of it and the larger elements on the left of it. It is a divide and conquer algorithm and requires an average time complexity of
O(nlogn)
.
Note:
1. Quick Sort is preferred over MergeSort for sorting Arrays.
2. MergeSort is preferred over QuickSort for Linked Lists
#include <stdio.h>
#include "util.h"
int partition(int arr[], int left, int right){
/* Choose the rightest element as pivot */
int pivot = arr[right];
int j = left - 1;
for (int i = left; i < right; i++){
/* If smaller than pivot, swap it */
if (arr[i] < pivot){
j++;
swap(&arr[j],&arr[i]);
}
}
swap(&arr[right],&arr[j+1]);
return (j+1) ;
}
void quickSort(int arr[], int left, int right){
if (left < right){
int seed = partition(arr, left, right);
quickSort(arr, left, seed-1);
quickSort(arr, seed+1, right);
}
}
int main()
{
int arr[] = {10, 7, 8, 3, 3, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
Summary
Worst case complexities:
Merge Sort —
nLogn
Bubble Sort —
n2
Quick Sort —
n2
Selection Sort —
n2
Insertion sort –
n2
Best case complexities:
Bubble Sort –
n
Inserttion Sort –
n
<script type="math/tex" id="MathJax-Element-16">n</script>