I'm trying to write a generic mergesort for arrays holding pointers to different structs. I'm at a point that the compiler doesn't give any errors, but the sort function doesn't give the right output yet.
The two structures and the two compare functions are defined as follows:
typedef struct Query {int day, rank;} Query;
typedef struct Event {int day, frame; char action;} Event;
int compEvents (const void *a, const void *b) {
return (*(Event**)a)->day - (*(Event**)b)->day;
}
int compQueries (const void *a, const void *b) {
return (*(Query**)a)->day - (*(Query**)b)->day;
}
If I use qsort, everything works fine and the output is correct:
qsort (queries, size, sizeof(Query*), compQueries);
If I use a different mergesort for each type, and don't use the compare functions, everything works fine too:
void queryMerge(Query **arr, int left, int mid, int right) {
Query **temp = newQueryArray(right-left+1);
int i = left, j = mid + 1, t = 0;
while (i <= mid && j <= right) {
if (arr[i]->day < arr[j]->day) temp[t++] = arr[i++];
else temp[t++] = arr[j++];
}
while (i <= mid) temp[t++] = arr[i++];
while (j <= right) temp[t++] = arr[j++];
for (int i = left; i <= right; i++) arr[i] = temp[i-left];
free(temp);
}
void querySort(Query **arr, int left, int right) {
if (left < right) {
int mid = left + (right - left)/2;
querySort(arr, left, mid);
querySort(arr, mid+1, right);
queryMerge(arr, left, mid, right);
}
}
void eventMerge(Event **arr, int left, int mid, int right) {
Event **temp = newEventArray(right-left+1);
int i = left, j = mid + 1, t = 0;
while (i <= mid && j <= right) {
if (arr[i]->day < arr[j]->day) temp[t++] = arr[i++];
else temp[t++] = arr[j++];
}
while (i <= mid) temp[t++] = arr[i++];
while (j <= right) temp[t++] = arr[j++];
for (int i = left; i <= right; i++) arr[i] = temp[i-left];
free(temp);
}
void eventSort(Event **arr, int left, int right) {
if (left < right) {
int mid = left + (right - left)/2;
eventSort(arr, left, mid);
eventSort(arr, mid+1, right);
eventMerge(arr, left, mid, right);
}
}
However, if I try to use a generic mergesort that would work for both types just like qsort, it isn't working correctly, even though the compiler and valgrind have no complaints:
void merge(void *arr, int left, int mid, int right, int width, int (*comp)(const void*, const void*)) {
void *temp = malloc((right - left + 1) * width);
int i = left, j = mid + 1, t = 0;
while (i <= mid && j <= right) {
if (comp((char*)arr + i*width, (char*)arr + j*width)) {
memcpy((char*)temp + t*width, (char*)arr + i*width, width);
i++; t++;
} else {
memcpy((char*)temp + t*width, (char*)arr + j*width, width);
j++; t++;
}
}
while (i <= mid) {
memcpy((char*)temp + t*width, (char*)arr + i*width, width);
i++; t++;
}
while (j <= right) {
memcpy((char*)temp + t*width, (char*)arr + j*width, width);
j++; t++;
}
for (int i = left; i <= right; i++) {
memcpy((char*)arr + i*width, (char*)temp + (i - left)*width, width);
}
free(temp);
}
void mergeSort(void *arr, int left, int right, int width, int (*comp)(const void*, const void*)) {
if (left < right) {
int mid = left + (right - left)/2;
mergeSort(arr, left, mid, width, comp);
mergeSort(arr, mid+1, right, width, comp);
merge(arr, left, mid, right, width, comp);
}
}
This is the way it is called:
mergeSort(queries, 0, size-1, sizeof(Query*), compQueries);
I tried different things to get the void pointers right, but I'm still not sure.
Any ideas?
Your usage of comparison function is broken:
if (comp((char*)arr + i*width, (char*)arr + j*width)))
is the same as
if (comp((char*)arr + i*width, (char*)arr + j*width)) != 0)
while you need
if (comp((char*)arr + i*width, (char*)arr + j*width)) < 0)
With your code you will ignore which was larger but only if they are same. As a result you do not sort at all.
Adding the <0 fixes the sorting.
Note:
Your specific function uses correct comparison result:
if (arr[i]->day < arr[j]->day)
Related
#include <stdio.h>
void msort(int *a, int n);
void msort_recursion(
int a[], int left,
int right);
void merge_arrays(int a[], int left, int middle,
int right); // merges the sorted portions of the array
int main() {
int a[] = {5, 2, 4, 1, 3};
int n = 5;
msort(a, n);
printf("[");
for (int i = 0; i < n; i++)
if (i == n - 1) {
printf("%d", a[i]);
} else {
printf("%d, ", a[i]);
}
printf("]\n");
return 0;
}
void msort(int *a, int n) { msort_recursion(a, 0, n - 1); }
void msort_recursion(int a[], int left, int right) {
if (left < right) {
int middle = left + (right - 1) / 2;
msort_recursion(a, left, middle);
msort_recursion(a, middle + 1,
right);
merge_arrays(a, left, middle,
right);
}
}
void merge_arrays(
int a[], int left, int middle,
int right) {
int left_size = middle - left + 1;
int right_size = right - middle;
int templ[left_size];
int tempr[right_size];
int i, j, k;
for (int i = 0; i < left_size; i++)
templ[i] = a[left + i];
for (int i = 0; i < right_size; i++)
tempr[i] = a[middle + 1 + i];
for (i = 0, j = 0, k = left; k <= right; k++) {
if ((i < left_size) && (j >= right_size || templ[i] <= tempr[j])) {
a[k] = templ[i];
i++;
} else {
a[k] = tempr[j];
j++;
}
}
}
Merge sort is implemented in Code, but when run, I receive the error code "signal: segmentation fault (core dumped)" which to my understanding, means that it has reached past the end of an array but I do not understand how this is... Merge sort is implemented in Code, but when run, I receive the error code "signal: segmentation fault (core dumped)" which to my understanding, means that it has reached past the end of an array but I do not understand how this is...
The reason is you called msort_recursion recursively to many times this happened because the middle is computed wrong and should be int middle = left + (right - left) / 2; notice it's the difference in position split in half.
make sure to read geeksforgeeks.org/merge-sort more carefully next time
void msort_recursion(int a[], int left, int right) {
if (left < right) {
int middle = left + (right - 1) / 2;
/* Here should be ^^^^^^^^^ right - left */
msort_recursion(a, left, middle);
msort_recursion(a, middle + 1,right);
merge_arrays(a, left, middle,right);
}
}
for msort_recursion, I was doing int middle = left + (right - 1) / 2 instead of int middle = left + (right - left) / 2
#include <stdio.h>
void msort(int *a, int n); // merge sort array a with n elements in place in C
void msort_recursion(int a[], int left, int right); // recursion where the array is continuously divided in half
// until there is only one element left
void merge_arrays(int a[], int left, int middle, int right); // merges the sorted portions of the array
int main() {
int a[] = {5, 2, 4, 1, 3};
int n = 5;
msort(a, n);
// print sorted array
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
void msort(int *a, int n) {
msort_recursion(a, 0, n - 1);
}
void msort_recursion(int a[], int left, int right) {
// as long as the left is less than the right, we will continuously divide the
// array
if (left < right) {
int middle = left + (right - left) / 2; // find the middle of the array
msort_recursion(a, left, middle); // recursion on the left side of the array
msort_recursion(a, middle + 1, right); // recursion on the right side of the array
merge_arrays(a, left, middle, right); // merge the sorted sections of the array
}
}
void merge_arrays(int a[], int left, int middle, int right) { // left is the index for the start of the array, middle is the
// middle index, right is the index of the last element in the
// right section of the array
int left_size = middle - left + 1; // size of left side of array
int right_size = right - middle; // size of right side of the array
// create 2 tepm sub arrarys and copy the portions into the sub arrays
int templ[left_size];
int tempr[right_size];
int i, j, k; // i is keeping track of left array, j is keeping track of right
// array, k is keeping track of original array a
for (int i = 0; i < left_size; i++)
// copy left side into left temp array
templ[i] = a[left + i];
for (int i = 0; i < right_size; i++)
// copy right side into right temp array
tempr[i] = a[middle + 1 + i];
// pick from the sorted left and right arrays to replace into the original
// array
for (i = 0, j = 0, k = left; k <= right; k++) {
if ((i < left_size) && (j >= right_size || templ[i] <= tempr[j])) {
// if the element in the left array is smaller than the element in the
// right array then replace it in array a as long as we don't reach the end
// of either the left or right arrays
a[k] = templ[i];
i++;
// otherwise, put the right element into the array a
} else {
a[k] = tempr[j];
j++;
}
}
}
Can anyone help me with this code because when it's executed it doesn't sort correctly the array. I can't figure out what's wrong.
I use this struct and get the data from a file
typedef struct record {
int id_field;
char string_field[20];
int int_field;
double double_field;
} record;
typedef int (*CompareFunction)(void *, void *);
and this is the quick sort:
void swap(void **a, void **b) {
void *tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void quick_sort(void **array, int left, int right, CompareFunction compare) {
int index;
if (left < right) {
index = partition(array, left, right, compare);
quick_sort(array, left, index - 1, compare);
quick_sort(array, index + 1, right, compare);
}
}
int partition(void **array, int left, int right, CompareFunction compare) {
int pivot = left + (right - left) / 2;
int i = left;
int j = right;
while (i < j) {
if (compare(array[i], array[pivot]) < 0) {
i++;
} else {
if (compare(array[j], array[pivot]) > 0) {
j--;
} else {
swap(&array[i], &array[j]);
i++;
j--;
}
}
}
swap(&array[pivot], &array[j]);
return j;
}
This is the compare function for int:
int compare_int_struct(void *ptr1, void *ptr2) {
int i1 = (*((record *) ptr1)).int_field;
int i2 = (*((record *) ptr2)).int_field;
if (i1 < i2) {
return -1;
}
if (i1 == i2) {
return 0;
}
return 1;
}
for example:
given array sorted array
233460 | 233460
4741192 | 1014671
1014671 | 1188961
496325 | 3119429
4476757 | 496325
3754104 | 2146160
4271997 | 2163766
4896376 | 2369159
2735414 | 3754104
2163766 | 2735414
2369159 | 4271997
1188961 | 4476757
3843159 | 4741192
2146160 | 3843159
It seems it orders in small blocks
The problems are in your partition routine.
You are selecting a pivot index, you then proceed to partition the (sub)array by comparing values indirectly via this index, and the value identified by l or r, respectively.
However, as you go, swapping values, sooner or later your selected pivot value will change its position in the array and you're now comparing to whatever happens to wind up at the pivot index.
Instead, you should save off the pivot value and compare to that. This has the added benefit that it saves array indexing within the inner loops:
int partition(void **array, int left, int right, CompareFunction compare) {
int pivot = left + (right - left) / 2;
int pivotValue = array[pivot]; // ********
int i = left;
int j = right;
while (i < j) {
if (compare(array[i], pivotValue) < 0) { // ********
i++;
} else {
if (compare(array[j], pivotValue) > 0) { // ********
j--;
} else {
swap(&array[i], &array[j]);
i++;
j--;
}
}
}
swap(&array[pivot], &array[j]);
return j;
}
And then there's that final swap. This is something you would use if you had chosen, up front, to move the selected pivot to the beginning or the end of the array and exclude that index from the remaining partitioning process. Several variants do this, but here that swap is just messing things up and should be removed.
Thank you all for your answers. I modified the the partition and now it works:
int pivot = left;
int i = left + 1;
int j = right;
while (i <= j) {
if (compare(array[i], array[pivot]) < 0) {
i++;
} else {
if (compare(array[j], array[pivot]) > 0) {
j--;
} else {
swap(&array[i], &array[j]);
i++;
j--;
}
}
}
swap(&array[pivot], &array[j]);
return j;
I wrote a merge sort program but I got wrong results.
I've seen other programs like this, but they don't help me solve my problem. I think the problem is in the merge function.
#include <stdio.h>
#include "stdafx.h"
#define Size 5
//this is the array
int arr[Size] = { 5, 4, 3, 2, 1 };
int sr[10];
void mergesort(int a[], int start, int end, int size);
void merge(int a[], int start, int end, int size);
int main(void) {
mergesort(arr, 0, 4, 5);
for (int i = 0; i < Size; i++) {
printf_s("%i", sr[i]);
}
printf_s("\n");
return 0;
}
void mergesort(int a[], int start, int end, int size) {
if (size < 2)
return;
int s = size / 2;
mergesort(a, start, (start + end) / 2, s);
mergesort(a, (start + end) / 2, end, s);
merge( a, start, end, s);
}
void merge(int a[], int start, int end, int size) {
int left = start;
int right = ((start + end) / 2) + 1;
for (int i = 0; i < size; i++) {
if (left < (start + end)/2) {
if (right >= end) {
sr[i] = arr[left];
left++;
} else
if (arr[left] < arr[right]) {
sr[i] = arr[left];
left++;
} else {
sr[i] = arr[right];
right++;
}
} else {
sr[i] = arr[right];
right++;
}
}
}
(1)
printf_s("%i",sr[i]); should be printf_s("%i ", arr[i]);
(2)
mergesort(a, start, (start + end) / 2, s);//E.g index:{0,1,2,3,4}, start:0, (start + end) / 2 : 2, s: 2, but 0(start),1,2(new end), this length is 3, not 2
mergesort(a, (start + end) / 2, end, s);//Duplicate start position and length should be size - s. E.g size:5, s:2, rest size is 3, not 2.
merge( a, start, end, s);//s should be size
should be like
mergesort(a, start, start + s - 1, s);
mergesort(a, start + s, end, size - s);
merge(a, start, end, size);
(3)
Change according to (2)
Change int right = ((start + end) / 2) +1; to int right = start + size / 2;.
(4)
Add int sr[size]; //Avoid using global variables. It is better to use malloc. E.g int *sr = malloc(size*sizeof(int));...free(sr);
(5)
if (left < (start+end)/2)
{
if (right >= end)
should be
if (left < start + size / 2)
{
if (right > end){//Should be >, not >=
(6) Write back to arr form sr is necessary
Whole code:
#include <stdio.h>
#include <stdlib.h>
void mergesort(int a[], int start, int end, int size);
void merge(int a[], int start, int end, int size);
int main(void){
int arr[] = {5,4,3,2,1};
int size = sizeof(arr)/sizeof(*arr);
mergesort(arr, 0, size - 1, size);
for (int i = 0; i < size; i++){
printf_s("%i ", arr[i]);
}
printf_s("\n");
return 0;
}
void mergesort(int a[], int start, int end, int size){
if (size < 2)
return;
int s = size / 2;
mergesort(a, start, start + s - 1, s);
mergesort(a, start + s, end, size - s);
merge(a, start, end, size);
}
void merge(int a[], int start, int end, int size){
int left = start;
int right = start + size / 2;
int right_start = right;
int *sr = (int*)malloc(size*sizeof(*sr));//Cast(int*) is not necessary in C.
for (int i = 0; i < size; i++){
if (left < right_start){
if (right > end){
sr[i] = a[left++];
} else if (a[left] < a[right]) {
sr[i] = a[left++];
} else {
sr[i] = a[right++];
}
} else {
sr[i] = a[right++];
}
}
for(int i = 0; i < size; ++i)//write back.
a[start + i] = sr[i];
free(sr);
}
Your code is invalid for multiple reasons:
mergesort splits the range into 2 halves of size size / 2, which is incorrect if size is not even.
the arguments to mergesort are incorrect, only the pointer and the size are needed.
the merge function gets values from the global array arr instead of the argument array and stores values into the global temporary array sr, but does not copy it back into the a array.
Here is a corrected and simplified version:
#include <stdio.h>
void mergesort(int a[], int size);
int main(void) {
int arr[] = { 5, 4, 3, 2, 1 };
int size = sizeof(arr) / sizeof(arr[0]);
mergesort(arr, size);
for (int i = 0; i < size; i++) {
printf_s("%i ", arr[i]);
}
printf_s("\n");
return 0;
}
void merge(int a[], int mid, int size) {
int sr[mid]; // temporary array for the left part
if (a[mid - 1] <= a[mid]) { // quick check for sorted case
return;
}
for (int i = 0; i < mid; i++) { // save left part
sr[i] = a[i];
}
// merge into array `a`.
for (int i = 0, left = 0, right = mid; left < mid; i++) {
if (right == size || sr[left] <= a[right]) {
a[i] = sr[left++];
} else {
a[i] = a[right++];
}
}
}
void mergesort(int a[], int size) {
if (size >= 2) {
int mid = (size + 1) / 2; // make left part no smaller than right part
mergesort(a, mid);
mergesort(a + mid, size - mid);
merge(a, mid, size);
}
}
For a bit of fun, I'm trying to implement an iterative variant of Introsort. The default implementation looks something like this:
void introsort_loop(int *a, size_t left, size_t right, size_t threshold, size_t depth) {
while(right-left > threshold) {
if(depth == 0) {
heapsort(a, left, right);
return;
}
--depth;
int p = partition(a, left, right);
introsort_loop(p, right, depth);
right = p-1;
}
}
void introsort(int *array, size_t num, size_t threshold) {
if(num > 1) {
introsort_loop(a, 0, num-1, threshold, log2(num)*2);
insertionsort(a, num);
}
}
I'm using the glibc implementation of qsort as the basis for an iterative introsort, since qsort happens to implement an iterative quicksort.
My implementation looks like this:
#include <limits.h>
#include <math.h>
#include "introsort.h"
// Stack node declarations used to store unfulfilled partition obligations.
typedef struct {
int lo;
int hi;
} stack_node;
// The next 4 #defines implement a very fast in-line stack abstraction.
// The stack needs log (total_elements) entries (we could even subtract
// log(threshold)). Since num has type size_t, we get as
// upper bound for log (num):
// bits per byte (CHAR_BIT) * sizeof(size_t).
#define STACK_SIZE (CHAR_BIT*sizeof(size_t))
#define PUSH(low, high) ((top->lo = (low)), (top->hi = (high)), ++top)
#define POP(low, high) (--top, ((low) = top->lo), ((high) = top->hi))
#define STACK_NOT_EMPTY (stack < top)
#define SWAP(a, i, j) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; }
#define PARENT(i) ((i-1)/2)
#define LEFT_CHILD(i) (((i)<<1)+1)
void heapify_i(int *a, int left, int right) {
int child, swap;
int root = left;
while((child = LEFT_CHILD(root)) <= right) {
swap = root;
if(a[swap] < a[child]) {
swap = child;
}
if(child+1 <= right && a[swap] < a[child+1]) {
swap = child+1;
}
if(swap == root) {
break;
} else {
SWAP(a, root, swap);
root = swap;
}
}
}
void heapsort_i(int *a, int left, int right) {
int start = left;
int end = right;
for(start = PARENT(end); start >= left; --start) {
heapify_i(a, start, end);
}
start = left;
while(start < end) {
SWAP(a, start, end);
heapify_i(a, start, --end);
}
}
void quicksort_i(int *a, size_t num, size_t threshold, size_t depth) {
//========== QUICKSORT ==========//
if(num > threshold) {
stack_node stack[STACK_SIZE];
stack_node *top = stack;
PUSH(-1, -1);
int low = 0;
int high = num-1;
int left, mid, right;
while(STACK_NOT_EMPTY) {
if(depth == 0) {
heapsort_i(a, low, high);
break;
} else {
--depth;
//========== PIVOT = MID (MEDIAN OF THREE) ==========
mid = low+(high-low)/2;
if(a[mid] < a[low]) {
SWAP(a, mid, low);
}
if(a[high] < a[mid]) {
SWAP(a, high, mid);
} else {
goto jump_qi;
}
if(a[mid] < a[low]) {
SWAP(a, mid, low);
}
jump_qi:;
//========== PARTITIONING ==========//
left = low+1;
right = high-1;
while(left < right) {
while(a[left] < a[mid]) {
++left;
}
while(a[mid] < a[right]) {
--right;
}
if(left < right) {
SWAP(a, left, right);
if(mid == left) {
mid = right;
} else if(mid == right) {
mid = left;
}
++left;
--right;
}
}
// Set up pointers for next iteration. First determine whether
// left and right partitions are below the threshold size. If so,
// ignore one or both. Otherwise, push the larger partition's
// bounds on the stack and continue sorting the smaller one.
if(right-low < threshold) {
if(high-left <= threshold) {
// ignore both small partitions
POP(low, high);
} else {
// ignore small left partition
low = left;
}
} else if(high-left <= threshold) {
// ignore small right partition
high = right;
} else if(right-low > high-left) {
// push larger left partition
PUSH(low, right);
low = left;
} else {
// push larger right partition
PUSH(left, high);
high = right;
}
}
}
}
//========== INSERTION SORT ==========//
int e, i, j;
for(i = 1; i <= num; ++i) {
e = a[i];
for(j = i-1; j >= 0 && e < a[j]; --j) {
a[j+1] = a[j];
}
a[j+1] = e;
}
}
void introsort_i(int *array, size_t num, size_t threshold) {
if(num > 1) {
quicksort_i(array, num-1, threshold, log2(num)*2);
}
}
For input of sizes 10 to 100'000random elements it appears to run fine, but when I test for a million elements, it suddenly slows down to a couple seconds, which is far too long for a single array with 1 million elements.
How do I fix this?
I'm trying to implement merge sort in C using arrays, here's my code:
#include <stdio.h>
#include <stdlib.h>
void merge(int s[], int low, int middle, int high)
{
int i,l=0,r=0;
int left[high/2], right[high/2];
for(i = low; i<=middle; i++) left[i-low] = s[i];
for(i = middle+1; i<=high; i++) right[i-middle-1] = s[i];
i = low;
while(l <= middle-low || r <= high - middle - 1)
{
if(left[l] <= right[r])
{
s[i++] = left[l];
l++;
}
else
{
s[i++] = right[r];
r++;
}
}
while(l <= middle-low)
{
s[i++] = left[l];
l++;
}
while(r <= high - middle - 1)
{
s[i++] = left[r];
r++;
}
}
void mergesort(int s[], int low, int high)
{
int i;
int middle;
if(low < high){
middle = (low + high)/2;
mergesort(s, low, middle);
mergesort(s, middle+1, high);
merge(s, low, middle, high);
}
}
int main()
{
int nums[] = {5, 345, 1, 120, 40, 3450};
int size = (sizeof(nums))/(sizeof(int));
int i;
for(i = 0; i < size; i++)
printf("%d ", nums[i]);
printf("\n");
mergesort(nums, 0, size);
for(i = 0; i < size; i++)
printf("%d ", nums[i]);
printf("\n");
return 0;
}
That outputs:
5 345 1 120 40 3450
0 1 4 5 40 120
Which is kind of close. Could someone point out my mistakes? Thank you.
You access the array out of bounds at several places. Your code uses C-style ranges, which have an inclusive lower bound L and an exclusive upper bound H. Exclusive means that the upper bound H is not a valid index in the (sub-)array. A typical loop over the range look like this:
for (i = L; i < U; i++) ...
or
i = L;
while (i < U) ...
A greater-than-or-equal operator <= in such loops should make you wary, as should suprious additions or subtraction of 1. They might be correct in some cases, but they are usually consequences of inconsitent array indexing.
Let's revise your code with the C-style ranges in mind:
int left[high/2], right[high/2];
The array sizes are wrong. The left array has middle - low elements and the right array has high - middle elements. If the array size high - low is odd, you have one more element in right than in left.
for(i = low; i<=middle; i++) left[i-low] = s[i];
You mistakenly put the middle element in the left array. It is the first element of the right array.
for(i = middle+1; i<=high; i++) right[i-middle-1] = s[i];
Same here, plus you access s[high] which is one beyond the array.
i = low;
while(l <= middle-low || r <= high - middle - 1)
The conditions should have < and no -1. More importantly, the conditions should both be true, otherwise you access the subarrays out of bounds; hence the operator should be ยด&&`.
if(left[l] <= right[r])
The <= is okay, though, for once.
while(l <= middle-low)
{
s[i++] = left[l];
l++;
}
while(r <= high - middle - 1)
{
s[i++] = left[r];
r++;
}
Here, it should be < again. Also note that you access left with the index r, which is probably just a typo owed to copy and paste.
if(low < high){
middle = (low + high)/2;
mergesort(s, low, middle);
mergesort(s, middle+1, high);
merge(s, low, middle, high);
}
Here, the second call to megesort should be to middle, not to middle + 1. Because the upper bound is exclusive and the lower is not, adjacent arrays share the same bounds.
Here's a sort that works:
void merge(int s[], int low, int middle, int high)
{
int i, l = 0, r = 0;
int left[middle - low];
int right[high - middle];
for (i = low; i < middle; i++) left[i - low] = s[i];
for (i = middle; i < high; i++) right[i - middle] = s[i];
i = low;
while (low + l < middle && middle + r < high) {
if (left[l] < right[r]) {
s[i++] = left[l];
l++;
} else {
s[i++] = right[r];
r++;
}
}
while (low + l < middle) {
s[i++] = left[l];
l++;
}
while (middle + r < high) {
s[i++] = right[r];
r++;
}
}
void mergesort(int s[], int low, int high)
{
int middle;
if (low + 1 < high) {
middle = (low + high) / 2;
mergesort(s, low, middle);
mergesort(s, middle, high);
merge(s, low, middle, high);
}
}
The code can still be improved. The different indices for the left and right subarrays make it difficult to maintain and test the code. If you have already learned about pointer arithmetic, you can do without the low bound entirely by passing array + low and the size as new array base, as EOF has suggested in a comment.
M Oehm provided an explanation and a fixed example of the original code in his answer.
Here is an alternate version that does a one time allocation of the temporary array and uses a pair of co-recursive functions to avoid copying of data. I'm not sure why top down merge sort is used so often, bottom up merge sort is non-recursive, a little bit faster, and simpler to understand.
On my system, Intel 2600K 3.4ghz, this example can sort 20 million 32 bit integers in about 2 seconds. (A bottom up merge sort would take about 1.9 seconds).
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee);
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee);
void MergeRuns(int a[], int b[], size_t ll, size_t rr, size_t ee);
void TopDownMergeSort(int a[], size_t n)
{
int *b;
if(n < 2) // if size < 2 return
return;
b = malloc(n * sizeof(int)); // one time allocation
TopDownSplitMergeAtoA(a, b, 0, n);
free(b);
return;
}
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee)
{
size_t rr;
if((ee - ll) == 1) // if size == 1 return
return;
rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoB(a, b, ll, rr);
TopDownSplitMergeAtoB(a, b, rr, ee);
MergeRuns(b, a, ll, rr, ee); // merge b to a
}
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee)
{
size_t rr;
if((ee - ll) == 1){ // if size == 1 copy a to b
b[ll] = a[ll];
return;
}
rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoA(a, b, ll, rr);
TopDownSplitMergeAtoA(a, b, rr, ee);
MergeRuns(a, b, ll, rr, ee); // merge a to b
}
void MergeRuns(int a[], int b[], size_t ll, size_t rr, size_t ee)
{
size_t o = ll; // b[] index
size_t l = ll; // a[] left index
size_t r = rr; // a[] right index
while(1){ // merge data
if(a[l] <= a[r]){ // if a[l] <= a[r]
b[o++] = a[l++]; // copy a[l]
if(l < rr) // if not end of left run
continue; // continue (back to while)
while(r < ee) // else copy rest of right run
b[o++] = a[r++];
break; // and return
} else { // else a[l] > a[r]
b[o++] = a[r++]; // copy a[r]
if(r < ee) // if not end of right run
continue; // continue (back to while)
while(l < rr) // else copy rest of left run
b[o++] = a[l++];
break; // and return
}
}
}