Introduction
Sorting algorithms arrange elements in a specific order (ascending or descending) within a data structure. They are fundamental to computer science and are used in many applications including database operations, search optimization, and data analysis. Understanding different sorting techniques helps in choosing the most efficient approach for specific scenarios.
Key Concepts
Sorting: Arranging elements in a specific order Time Complexity: Computational time required relative to input size Space Complexity: Additional memory required by the algorithm Stability: Maintaining relative order of equal elements In-place Sorting: Sorting without requiring additional storage space
Bubble Sort
Algorithm and Implementation
#include <stdio.h>
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// Swap elements
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original array: ");
printArray(arr, n);
bubbleSort(arr, n);
printf("Sorted array: ");
printArray(arr, n);
return 0;
}
Characteristics:
- Time Complexity: O(n²)
- Space Complexity: O(1)
- Stable: Yes
- In-place: Yes
Selection Sort
Algorithm and Implementation
#include <stdio.h>
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
// Find minimum element in remaining array
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// Swap minimum element with first element
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
int main() {
int arr[] = {29, 10, 14, 37, 13};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Before sorting: ");
printArray(arr, n);
selectionSort(arr, n);
printf("After sorting: ");
printArray(arr, n);
return 0;
}
Characteristics:
- Time Complexity: O(n²)
- Space Complexity: O(1)
- Stable: No
- In-place: Yes
Insertion Sort
Algorithm and Implementation
#include <stdio.h>
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// Move elements greater than key one position ahead
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
int main() {
int arr[] = {5, 2, 4, 6, 1, 3};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original: ");
printArray(arr, n);
insertionSort(arr, n);
printf("Sorted: ");
printArray(arr, n);
return 0;
}
Characteristics:
- Time Complexity: O(n²) worst case, O(n) best case
- Space Complexity: O(1)
- Stable: Yes
- In-place: Yes
Quick Sort
Algorithm and Implementation
#include <stdio.h>
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
// Swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Swap arr[i+1] and arr[high] (pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
int main() {
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original array: ");
printArray(arr, n);
quickSort(arr, 0, n - 1);
printf("Sorted array: ");
printArray(arr, n);
return 0;
}
Characteristics:
- Time Complexity: O(n log n) average, O(n²) worst case
- Space Complexity: O(log n)
- Stable: No
- In-place: Yes
Merge Sort
Algorithm and Implementation
#include <stdio.h>
#include <stdlib.h>
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int *leftArr = (int*)malloc(n1 * sizeof(int));
int *rightArr = (int*)malloc(n2 * sizeof(int));
// Copy data to temporary arrays
for (int i = 0; i < n1; i++)
leftArr[i] = arr[left + i];
for (int j = 0; j < n2; j++)
rightArr[j] = arr[mid + 1 + j];
// Merge the temporary arrays back
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (leftArr[i] <= rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
k++;
}
// Copy remaining elements
while (i < n1) {
arr[k] = leftArr[i];
i++;
k++;
}
while (j < n2) {
arr[k] = rightArr[j];
j++;
k++;
}
free(leftArr);
free(rightArr);
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Given array: ");
printArray(arr, n);
mergeSort(arr, 0, n - 1);
printf("Sorted array: ");
printArray(arr, n);
return 0;
}
Characteristics:
- Time Complexity: O(n log n)
- Space Complexity: O(n)
- Stable: Yes
- In-place: No
Algorithm Comparison
Performance Analysis
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
void testSortingAlgorithm(void (*sortFunc)(int[], int),
const char* name,
int arr[],
int size) {
int *testArr = (int*)malloc(size * sizeof(int));
memcpy(testArr, arr, size * sizeof(int));
clock_t start = clock();
sortFunc(testArr, size);
clock_t end = clock();
double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("%s: %.6f seconds\n", name, time_taken);
free(testArr);
}
void generateRandomArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] = rand() % 1000;
}
}
int main() {
const int SIZE = 1000;
int *arr = (int*)malloc(SIZE * sizeof(int));
srand(time(NULL));
generateRandomArray(arr, SIZE);
printf("Comparing sorting algorithms with %d elements:\n", SIZE);
testSortingAlgorithm(bubbleSort, "Bubble Sort", arr, SIZE);
testSortingAlgorithm(selectionSort, "Selection Sort", arr, SIZE);
testSortingAlgorithm(insertionSort, "Insertion Sort", arr, SIZE);
free(arr);
return 0;
}
Specialized Sorting Applications
Sorting Structures
#include <stdio.h>
#include <string.h>
struct Student {
int rollNo;
char name[30];
float marks;
};
void sortStudentsByMarks(struct Student students[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (students[j].marks < students[j + 1].marks) {
struct Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
}
void displayStudents(struct Student students[], int n) {
printf("Roll No\tName\t\tMarks\n");
printf("-------\t----\t\t-----\n");
for (int i = 0; i < n; i++) {
printf("%d\t%-15s\t%.2f\n",
students[i].rollNo,
students[i].name,
students[i].marks);
}
}
int main() {
struct Student students[] = {
{101, "Alice", 85.5},
{102, "Bob", 92.0},
{103, "Charlie", 78.5},
{104, "Diana", 96.5},
{105, "Eve", 88.0}
};
int n = sizeof(students) / sizeof(students[0]);
printf("Before sorting:\n");
displayStudents(students, n);
sortStudentsByMarks(students, n);
printf("\nAfter sorting by marks (descending):\n");
displayStudents(students, n);
return 0;
}
Algorithm Selection Guidelines
When to Use Each Algorithm:
Bubble Sort:
- Educational purposes
- Very small datasets
- Nearly sorted data
Selection Sort:
- Small datasets
- Memory is limited
- Simple implementation needed
Insertion Sort:
- Small to medium datasets
- Nearly sorted data
- Online sorting (data arrives continuously)
Quick Sort:
- Large datasets
- Average case performance important
- Memory efficiency required
Merge Sort:
- Large datasets
- Stable sorting required
- Worst-case performance guarantee needed
Best Practices
- Choose appropriate algorithm based on data size and requirements
- Consider stability if equal elements’ order matters
- Analyze space constraints for memory-limited environments
- Test with different data patterns (sorted, reverse sorted, random)
- Use built-in library functions for production code when available
Summary
Sorting algorithms are essential tools in programming with different characteristics and use cases. Simple algorithms like bubble, selection, and insertion sort are easy to understand but inefficient for large datasets. Advanced algorithms like quick sort and merge sort offer better performance for larger data. The choice of sorting algorithm depends on factors including data size, memory constraints, stability requirements, and performance needs.
Part of BCA Programming with C Course (UGCOA22J201)