Merge sort is an easy-to-implement stable sorting algorithm which is particularly useful in competitive programming when you want to count reverse pairs. Proficient programmer can coding and debugging it within even 5 minutes. The following is a typical implementation of merge sort in C.
#include <stdlib.h>
#include <time.h>
void merge_sort(int *arr, int low, int high) {
// base case
if (high <= low) {
return;
}
// recursive case
int mid = (low + high) / 2;
merge_sort(arr, low, mid);
merge_sort(arr, mid + 1, high);
// -- merge
int buffer[high - low + 1];
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high) {
if (arr[i] <= arr[j]) {
buffer[k++] = arr[i++];
} else {
buffer[k++] = arr[j++];
}
}
while (i <= mid) {
buffer[k++] = arr[i++];
}
while (j <= high) {
buffer[k++] = arr[j++];
}
// -- copy back
for (k = 0; k < high - low + 1; k++) {
arr[low + k] = buffer[k];
}
return;
}
int main() {
int len = 4000000;
int *arr = malloc(len * sizeof(int));
srand((unsigned int) time(NULL));
for (int i = 0; i < len; i++) {
arr[i] = rand();
}
merge_sort(arr, 0, len - 1);
return 0;
}
Looks good... right? If you directly compile and run this code, you will end up with segmentation fault.
$ gcc -Wall -Wextra -Wpedantic -Werror -std=gnu17 -O2 main.c -o main
$ ./main
Segmentation fault (core dumped)
But why? Notice that len
in main()
is a pretty large number, and the space complexity of out implementation is O(len)
due to the stack allocated array buffer
in merge_sort()
. The default stack capacity in many systems is 8MiB. We can verify this by ourselves.
$ ulimit -s
8192
ulimit
is a Bash built-in command. With -s
option, it will tell us the stack capacity for a process forked from shell in KiB. We type in ./main
in the shell and hit Enter. The shell calls fork()
first to copy itself to spawn a child process, and then child process calls execv()
to replace the program with ./main
. So, the default stack capacity for ./main
is also 8MiB.
However, int buffer[4000000]
is about 15MiB, which exceeds the 8MiB limit. The most portable fix of this issue it to allocate the buffer on the heap instead of stack.
#include <stdlib.h>
...
void merge_sort(int *arr, int low, int high) {
...
int *buffer = malloc((high - low + 1) * sizeof(int));
...
free(buffer); // DO NOT forget this!
return;
}
...
Compile and run the code again, we won't have any issues.
$ gcc -Wall -Wextra -Wpedantic -Werror -std=gnu17 -O2 main.c -o main
$ ./main
$ echo $?
0
Comments 2 comments
"particularly useful in competitive programming" is suspicious lol
Merge Sort is only used when need to count inverse pairs, but BIT is a better choice in most cases
@template Wow, you are the first commenter of my blog! Thanks for your comment! Maybe not "particularly" useful but still useful :)
By the way, thanks for letting me know there is another option for counting inverse pairs. I will look into it!