C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
171: Implementing Merge Sort
I've always found Merge Sort to be the "aha!" moment for most developers learning divide-and-conquer. It's not as intuitive as Bubble Sort, but it's infinitely more powerful. To get our hands dirty, we aren't just going to sort random numbers; let's imagine we're handling a batch of temperature readings from a remote sensor that arrived out of order. We need them sorted to find the median temperature of the day.
Cutting the data in half
The core philosophy of Merge Sort is: "I don't know how to sort this whole mess, but I know how to sort two tiny lists." So, we start by recursively splitting the array. I usually write a helper function that takes the array, a left index, and a right index.
void mergeSort(int arr[], int l, int r) {
if (l < r) {
// Same as (l+r)/2, but avoids overflow for giant arrays
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
You'll notice I'm calling mergeSort twice before I ever call merge. This is the magic of the call stack. The program keeps diving deeper and deeper into the array until it's looking at single-element arrays. A single element is, by definition, sorted. Now we just have to put them back together.
The heavy lifting of merging
The merge function is where the actual sorting happens. We create two temporary arrays to hold the split data, then we pick the smallest available element from either side and move it back into the original array. Here is how I usually structure the logic:
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2]; // Using Variable Length Arrays for simplicity here
for (int i = 0; i < n1; i++) L[i] = arr[l + i];
for (int j = 0; j < n2; j++) R[j] = arr[m + 1 + j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// ... (copy remaining elements)
}
A quick detour into a common bug
When I first wrote this logic years ago, I made a classic mistake. I forgot that one of the two temporary arrays almost always has elements left over after the while loop finishes. I assumed the loop would handle everything. When I ran it on my sensor data, the end of my array was just... missing. Or rather, it contained stale data from the previous unsorted state.
I realized I needed two more small loops to "clean up" whatever was left in L or R. If you don't do this, you're effectively deleting data from your dataset. Here is the fix I added to the end of the merge function:
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
Putting it all together
Now, if we wrap this in a main function and pass in our temperature readings—say, {22, 18, 25, 19, 21}—the algorithm will split them down to individual units, then merge them back as {18, 22}, {19, 25}, and finally {18, 19, 21, 22, 25}. It's a bit more memory-intensive than Quick Sort because of those temporary arrays, but it guarantees O(n log n) time complexity every single time, regardless of how messy the input is.
📋 Practical Task
Exercise: Sorting High-Resolution Timestamps
Imagine you are building a logging system for a flight controller. You have an array of integers representing microsecond timestamps of events. However, because the events were captured by different threads, they are out of order.
Your task: Implement the mergeSort and merge functions in C to sort an array of 10 timestamps: {1050, 1010, 1080, 1020, 1000, 1070, 1030, 1090, 1040, 1060}.
Ensure your implementation correctly handles the "cleanup" loops for any remaining elements in the temporary arrays. Print the final sorted array to the console to verify the timestamps are in ascending order.
There are no comments for now.