Related
Comparing the running time of these two sorts, for some reason I get that they work in almost the same time, and on sorted arrays, shell sorting works 4 times faster. Although this can't be, because in the best case, shell sorting is performed in time n(log n)^2, while merge sorting is performed in nlogn time, which is faster than shell sorting. What could be the problem? I tried it on different PCs, but shell sorting is still faster.
UPD: For arrays filled randomly, it works as it should, but for already sorted ones, shell sorting works twice as fast.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define DIFF 3276
double wtime(void);
void randArray(int* array, int size);
void shellSort(int a[], int size);
void merge(int arr[], int l, int m, int r);
void mergeSort(int arr[], int l, int r);
int main(void)
{
FILE* file1;
FILE* file2;
int* array = NULL;
unsigned seed = time(NULL);
if (!(file1 = fopen("shell.txt", "w"))) {
return 1;
}
for (int step = DIFF; step <= 32760; step += DIFF) {
srand(seed);
array = (int*)malloc(step * sizeof(int));
double start, end;
randArray(array, step);
start = wtime();
shellSort(array, step);
end = wtime();
free(array);
fprintf(file1, "%d\t%lf\n", step, end - start);
}
fclose(file1);
if (!(file2 = fopen("merge.txt", "w"))) {
return 1;
}
for (int step = DIFF; step <= 32760; step += DIFF) {
srand(seed);
array = (int*)malloc(step * sizeof(int));
double start, end;
randArray(array, step);
start = wtime();
mergeSort(array, 0, step - 1);
end = wtime();
free(array);
fprintf(file2, "%d\t%lf\n", step, end - start);
}
fclose(file2);
}
void randArray(int* array, int size)
{
for (int i = 0; i < size; i++) {
array[i] = i;
}
}
double wtime(void)
{
struct timeval t;
gettimeofday(&t, NULL);
return (double)t.tv_sec + (double)t.tv_usec * 1E-6;
}
void shellSort(int a[], int size)
{
int i, j;
int s = size / 2;
while (s > 0) {
for (i = s; i < size; i++) {
int temp = a[i];
for (j = i - s; (j >= 0) && (a[j] > temp); j -= s)
a[j + s] = a[j];
a[j + s] = temp;
}
s /= 2;
}
}
void merge(int arr[], int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
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++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r)
{
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
Your shell sort works in-place and for a sorted array it never swaps a single element. So it only has the cost of the compares and the branch predictor will predict the compare perfectly every time.
Your merge sort on the other hand copies the data to temp arrays and back in every step. That's 2 * log(n) copies of the whole array. The extra memory needed might also exceed the L1 cache of your CPU for the larger test runs making this magnitudes worse. You can cut that in half by alternating between 2 arrays, well almost log(n) + 1 copies worst case. Branch prediction for the sorted case is perfect too, it's just the copies that cost you.
Hello I am working on an application of the merge sort algorithm however after a few hours I am unable to find a solution. Any help or hint would be appreciated. I have tried to debug my code but after many attempts I have not been successful in seeing what the mistake is.
The problem is that the algorithm gives back a wrong result. Example:
input: (10, 5, 16, 2)
output: (2, 5, 5, 10)
#include <stdio.h>
void mergesort(int A[], int l, int r);
void merge(int A[], int l, int q, int r);
int main() {
int n;
scanf("%d", &n);
int tablica[n];
for (int i = 0; i < n; i++)
scanf("%d", &tablica[i]);
mergesort(tablica, 0, n - 1);
for (int i = 0; i < n; i++)
printf("%d ", tablica[i]);
return 0;
}
void mergesort(int A[], int l, int r) {
if (l < r) {
int q = (l + r) / 2;
mergesort(A, l, q);
mergesort(A, q + 1, r);
merge(A, l, q, r);
}
}
void merge(int A[], int l, int q, int r) {
int n = q - l + 1;
int m = r - q;
int B[n];
int C[m];
for (int i = 0; i < n; i++)
B[i] = A[i];
for (int i = 0; i < m; i++)
C[i] = A[q + 1 + i];
int i = 0;
int j = 0;
for (int k = l; k <= r; k++) {
if (B[i] <= C[j] || j >= m) {
A[k] = B[i];
i++;
} else {
A[k] = C[j];
j++;
}
}
}
Without knowing what exactly doesn't work (Does it not compile? Do you get wrong output for some inputs?) it's hard to help. At least one error is here:
if(B[i] <= C[j] || j >= m)
Should be
if(j >= m || i < n && B[i] <= C[j])
It is both important to check j >= m before you check the inequality, and add the i < n check.
Without the latter once you fully consume the B array, B[i] will go beyond the array boundaries, and you will get undefined behavior.
Without the former once j >= m the condition B[i] <= C[j] will be checked before j >= m, also triggering undefined behavior.
UPDATE: with the actual example you provided, the second error narrows down to replacing B[i] = A[i] with B[i] = A[l + i]. With these two changes the example you provided works.
The function merge has several bugs.
For example the size of the array C shall be calculated like
int m = r - q + 1;
instead of
int m = r - q;
Instead of this for loop
for(int i = 0; i < n; i++)
B[i] = A[i];
you have to write
for(int i = 0; i < n; i++)
B[i] = A[l + i];
This if statement
if(B[i] <= C[j] || j >= m)
can result in undefined behavior because there is no check of the validity of the used indices i and j whether they belong to the valid ranges.
The functions can be defined the following way as it is shown in the demonstrative program below.
#include <stdio.h>
void merge( int a[], size_t left, size_t middle, size_t right )
{
size_t n1 = middle - left;
size_t n2 = right - middle;
int a1[n1];
int a2[n2];
for ( size_t i = 0; i < n1; i++ )
{
a1[i] = a[left + i];
}
for ( size_t i = 0; i < n2; i++ )
{
a2[i] = a[middle + i];
}
size_t i = left, i1 = 0, i2 = 0;
while ( i1 < n1 && i2 < n2 )
{
a[i++] = a2[i2] < a1[i1] ? a2[i2++] : a1[i1++];
}
while ( i1 < n1 ) a[i++] = a1[i1++];
while ( i2 < n2 ) a[i++] = a2[i2++];
}
void mergesort( int a[], size_t left, size_t right )
{
if ( left + 1 < right )
{
size_t middle = ( left + right ) / 2;
mergesort( a, left, middle );
mergesort( a, middle, right );
merge( a, left, middle, right );
}
}
int main(void)
{
size_t n;
if ( scanf( "%zu", &n ) == 1 && n != 0 )
{
int a[n];
for ( size_t i = 0; i < n; i++ ) scanf( "%d", a + i );
mergesort( a, 0, n );
for ( size_t i = 0; i < n; i++ )
{
printf( "%d ", a[i] );
}
putchar( '\n' );
}
return 0;
}
If the input is
4
10 5 16 2
then the output will be
2 5 10 16
There is a bug in the merge function:
if (B[i] <= C[j] || j >= m) accesses B[i] and C[j] before testing if i and j are below their respective boundaries. The test should be:
if (i < n && (j >= m || B[i] <= C[j])) {
A[k] = B[i++];
} else {
A[k] = C[j++];
}
Note also these remarks:
you do not need to save the second half of the array because its elements are copied before they get overwritten.
in void mergesort(int A[], int l, int r) r should be excluded, to avoid confusing and error prone +1/-1 adjustments.
similarly, in void merge(int A[], int l, int q, int r), q should be the start of the second half and r the index of the element after the end of the slice.
l looks confusingly similar to 1
Here is a modified version:
#include <stdio.h>
void mergesort(int A[], int low, int high);
void merge(int A[], int low, int mid, int high);
int main() {
int n;
if (scanf("%d", &n) != 1 || n <= 0)
return 1;
int tablica[n];
for (int i = 0; i < n; i++) {
if (scanf("%d", &tablica[i]) != 1)
return 1;
}
mergesort(tablica, 0, n);
for (int i = 0; i < n; i++)
printf("%d ", tablica[i]);
printf("\n");
return 0;
}
void mergesort(int A[], int low, int high) {
if (high - low >= 2) {
int mid = low + (high - low) / 2;
mergesort(A, low, mid);
mergesort(A, mid, high);
merge(A, low, mid, high);
}
}
void merge(int A[], int low, int mid, int high) {
int n = mid - low;
int B[n];
for (int i = 0; i < n; i++)
B[i] = A[low + i];
int i = 0;
int j = mid;
int k = low;
while (i < n) {
if (j >= high || B[i] <= A[j]) {
A[k++] = B[i++];
} else {
A[k++] = C[j++];
}
}
}
I am implementing merge sort in C. I have a merge function - merge(int array[], int start, int middle, int end) and a mergeSort function - mergeSort(int *array, unsigned int size).
The merge sort works perfectly if the first half of the original array and the second half of the original array are sorted (ex: 5,6,7,8,1,2,3,4). This is because my merge function is getting passed the original array no matter what, and it works when it is given 2 sorted arrays (as expected). My issue is when they aren't. Every time I call merge, my original array isn't being modified, even though I have programmed it to do so. Can anyone figure out where my issue is? The code is below.
When I run this code on input {10,9,8,7,6,5,4,3,2,1} it returns {5,4,3,2,1,0,10,9,8,7,6}.
void merge(int arr[], int l, int m, int r) {
int size1 = m - l + 1;
int size2 = r - m;
int arr1[size1];
int arr2[size2];
int i;
for ( i = 0; i < size1; i++ ) {
arr1[i] = arr[l + i];
}
for ( i = 0; i < size2; i++ ) {
arr2[i] = arr[m + i + 1];
}
i = 0;
int j = 0;
int k = 0;
while ( i < size1 && j < size2 ) {
if ( arr1[i] < arr2[j] ) {
arr[k] = arr1[i];
i++;
k++;
} else {
arr[k] = arr2[j];
j++;
k++;
}
}
while ( i < size1 ) {
arr[k] = arr1[i];
i++;
k++;
}
while ( j < size2 ) {
arr[k] = arr2[j];
j++;
k++;
}
}
void mergeSort(int *array, unsigned int size) {
int start = 0;
int middle = (size / 2) - 1;
int end = size - 1;
if ( size < 2 ) {
return;
}
int m = ( size / 2 );
int arr1[m];
int arr2[size - m];
int i;
for ( i = 0; i < middle + 1; i++ ) {
arr1[i] = array[i];
printf("%d\n", arr1[i]);
}
for ( i = middle + 1; i < size; i++ ) {
arr2[i - (middle + 1)] = array[i];
}
mergeSort(arr1, m);
mergeSort(arr2, size - m);
merge(array, start, middle, end);
}
In mergeSort, after doing mergeSort(arr1, m) and mergeSort(arr2, size - m), you are not actually doing anything with arr1 and arr2.
For a simple fix, I suggest not using the variables arr1 and arr2 and calling mergeSort directly on parts of array, like so:
void mergeSort(int* array, unsigned int size) {
int start = 0;
int middle = (size / 2);
int end = size - 1;
if ( size < 2 ) {
return;
}
mergeSort(array, middle);
mergeSort(array + middle, size - middle);
merge(array, start, middle - 1, end);
}
There are multiple issues in your code:
the mergeSort function splits the array into 2 local arrays and calls itself recursively to sort them, but the merge phase does not take these sorted arrays as inputs. You should instead use portions of the argument array directly.
the size computations are cumbersome, with many adjustments that would pose problems for small values of size. Use a simple convention: pass end as the offset of the first element past the end of the array. This way the size is computing simply by subtracting start from end.
the merge function initializes k to 0 instead of l.
Here is a corrected version:
void merge(int arr[], int start, int m, int end) {
int size1 = m - start;
int size2 = end - m;
int arr1[size1];
int arr2[size2];
int i, j, k;
for (i = 0; i < size1; i++) {
arr1[i] = arr[start + i];
}
for (i = 0; i < size2; i++) {
arr2[i] = arr[m + i];
}
i = j = 0;
k = start;
while (i < size1 && j < size2) {
if (arr1[i] < arr2[j]) {
arr[k++] = arr1[i++];
} else {
arr[k++] = arr2[j++];
}
}
while (i < size1) {
arr[k++] = arr1[i++];
}
while (j < size2) {
arr[k++] = arr2[j++];
}
}
void mergeSort(int *array, unsigned int size) {
if (size >= 2) {
int m = size / 2;
mergeSort(array, m);
mergeSort(array + m, size - m);
merge(array, 0, m, size);
}
Bob__ suggested a simplification, saving only the first half of the array to arr1 and removing the need for arr2. Here is a modified version, also removing start which is always 0 and some other simplifications:
void merge(int arr[], size_t m, size_t size) {
int arr1[m];
size_t i, j, k;
for (i = j = k = 0; j < m; j++) {
arr1[j] = arr[j];
}
while (i < m && j < size) {
if (arr1[i] < arr[j]) {
arr[k++] = arr1[i++];
} else {
arr[k++] = arr[j++];
}
}
while (i < m) {
arr[k++] = arr1[i++];
}
}
void mergeSort(int *array, size_t size) {
if (size >= 2) {
size_t m = size / 2;
mergeSort(array, m);
mergeSort(array + m, size - m);
merge(array, m, size);
}
Note however that allocating arr1 with automatic storage, aka on the stack, may cause undefined behavior for large arrays, typically larger than a few hundreds of thousand of elements. Allocating a temporary array from the heap avoid this problem at the cost of extra overhead and the possibility for allocation failure.
Sorted merge on every recursive is done on local array created on stack and it is not carry forwarded to previous function call. End result of entire recursive call is nothing but merge of {10,9,8,7,6} and {5,4,3,2,1}.
You can find the debug verification here.
Note: output has variation in my debug code as caller of mergeSort can have different implementation
Updated snippet as follows,
#include <stdio.h>
void merge(int arr[], int l, int m, int r) {
int size1 = m - l;
int size2 = r - m;
int arr1[size1];
int arr2[size2];
int i;
for ( i = 0; i < m; i++ ) {
arr1[i] = arr[l + i];
}
for ( i = 0; i+m < r; i++ ) {
arr2[i] = arr[m + i ];
}
i = 0;
int j = 0;
int k = 0;
while ( i < size1 && j < size2 ) {
if ( arr1[i] < arr2[j] ) {
arr[k++] = arr1[i++];
} else {
arr[k++] = arr2[j++];
}
}
while ( i < size1 ) {
arr[k++] = arr1[i++];
}
while ( j < size2 ) {
arr[k++] = arr2[j++];
}
}
void mergeSort(int* array, unsigned int size) {
if ( size < 2 ) {
return;
}
int start = 0;
int middle = (size / 2);
int end = size;
mergeSort(array, middle);
mergeSort(&array[middle], end-middle);
merge(array, start, middle, end);
}
int main()
{
int a[] = {10,9,8,7,6,5,4,3,2,1};
const int size = sizeof(a)/sizeof(a[0]);
mergeSort(a,size);
for(int i=0;i<size;i++)
printf("%d ", a[i]);
}
output : 1 2 3 4 5 6 7 8 9 10
I have been going through Introduction to Algorithms, and have been trying to implement the MERGE-SORT algorithm in C programming language to gain a better understanding of it.
The book presents two pseudo-codes:
and
While I do understand the above procedures, I must be missing something during the implementation.
I must be missing something from the pseudo-code but cannot figure it out yet. Any suggestions as to why this is happening would be appreciated.
EDIT: Updated Code and Output
/* C program for Merge Sort */
#include<stdlib.h>
#include<stdio.h>
void MERGE(int [], int , int , int );
void printArray(int [], int );
void MERGE_SORT(int [], int , int );
int main(void)
{
int A[] = { 12, 11, 13, 5, 6, 7, 2, 9 };
int arr_size = sizeof(A) / sizeof(A[0]);
printf("Given array is \n");
printArray(A, arr_size);
MERGE_SORT(A, 0, arr_size); //Fixed: Index to start from zero
printf("\nSorted array is \n");
printArray(A, arr_size);
return 0;
}
void MERGE(int A[], int p, int q, int r)
{
int i = 0;
int j = 0;
int n1 = q - p + 1; //Computing length of sub-array 1
int n2 = r - q; //Computing length of sub-array 2
int *L = malloc((n1 + 2) * sizeof(*L + 1)); //Creating Left array
int *R = malloc((n2 + 2) * sizeof(*R + 1)); //Creating Right array
for (int i = 0; i <= n1; i++) { //Fixed: <=, i start from 0
L[i] = A[p + i - 1];
}
for (int j = 0; j <= n2; j++) { //Fixed: <=, i start from 0
R[j] = A[q + j];
}
L[n1 + 1] = 99; //Placing Sentinel at the end of array
R[n2 + 1] = 99;
i = 1;
j = 1;
/*Prior to the first iteration k = p, so the subarray is empty.
Both L[i] and R[j] are the smallest elements of their arrays and have not
been copied back to A*/
for (int k = p; k <= r; k++) { //Fixed: <=
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
}
else { //Fixed: Assignment and not condition check for A[k]
A[k] = R[j];
j++;
}
}
free(L);
free(R);
}
void MERGE_SORT(int A[], int p, int r)
{
//During first iteration p = 1 & r = 8
if (p < r) {
int q = (p + r) / 2;
MERGE_SORT(A, p, q);
MERGE_SORT(A, q + 1, r);
MERGE(A, p, q, r);
}
}
/* 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");
}
Looked in the pseudo code and found out that some things have been mistakenly written wrong.
1. You need to be careful with the array index to start from 0 or 1
2. Merge last part in for loop is actually an assignment instead for conditional check.
Edit: Have updated the code to fix for the error Stack around the variable A was corrupted
Please find the corrected code here(Lookout for //Fixed for fixes)
/* C program for Merge Sort */
#include<stdlib.h>
#include<stdio.h>
void MERGE(A, p, q, r);
void printArray(Arr, size);
void MERGE_SORT(A, p, r);
int main(void)
{
int A[] = { 12, 11, 13, 5, 6, 7, 2, 9 };
int arr_size = sizeof(A) / sizeof(A[0]);
printf("Given array is \n");
printArray(A, arr_size);
MERGE_SORT(A, 0, arr_size - 1); //Fixed: Index to start from zero, arr_size - 1
printf("\nSorted array is \n");
printArray(A, arr_size);
return 0;
}
void MERGE(int A[], int p, int q, int r)
{
int i = 0;
int j = 0;
int n1 = q - p + 1; //Computing length of sub-array 1
int n2 = r - q; //Computing length of sub-array 2
int *L = malloc((n1+1) * sizeof(*L+1)); //Creating Left array
int *R = malloc((n2+1) * sizeof(*R+1)); //Creating Right array
for (int i = 0; i < n1; i++) { //Fixed: i start from 0
L[i] = A[p + i];
}
// int arr_size = sizeof(A) / sizeof(A[0]);
for (int j = 0; j < n2; j++) { //Fixed: j start from 0
R[j] = A[q + j + 1];
}
L[n1] = 99; //Placing Sentinel at the end of array
R[n2] = 99;
i = 0; //Fixed: i and j to start from 0
j = 0;
/*Prior to the first iteration k = p, so the subarray is empty.
Both L[i] and R[j] are the smallest elements of their arrays and have not
been copied back to A*/
for (int k = p; k <= r; k++) { //Fixed: <=
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
}
else { //Fixed: Assignment and not condition check for A[k]
A[k] = R[j];
j++;
}
}
free(L);
free(R);
}
void MERGE_SORT(int A[], int p, int r)
{
//During first iteration p = 1 & r = 8
if (p < r) {
int q = (p + r) / 2;
MERGE_SORT(A, p, q);
MERGE_SORT(A, q + 1, r);
MERGE(A, p, q, r);
}
}
/* 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", size);
}
Hope it helps.
Revert for any doubts.
here are some changes i have done to your code `
#include<stdlib.h>
#include<stdio.h>
void MERGE(int *A,int p,int q,int r);
void printArray(int *Arr,int size);
void MERGE_SORT(int *A,int p,int r);
int main(void){
int A[] = { 12, 11, 13, 5, 6, 7, 2, 9 };
int arr_size = sizeof(A) / sizeof(A[0]);
printf("Given array is \n");
printArray(A, arr_size);
MERGE_SORT(A, 0, arr_size -1); // pass the indices of the array
printf("\nSorted array is \n");
printArray(A, arr_size);
return 0;
}
void MERGE(int A[], int p, int q, int r){
int i = 0;
int j = 0;
int k; //declair it here
int n1 = q - p + 1; //Computing length of sub-array 1
int n2 = r - q; //Computing length of sub-array 2
int *L = malloc((n1) * sizeof(*L+1)); //Creating Left array
int *R = malloc((n2) * sizeof(*R+1)); //Creating Right array
for (int i = 0; i < n1; i++) { //start coping from zero
L[i] = A[p + i];
}
for (int j = 0; j < n2; j++) {
R[j] = A[q +1 + j];
}
// L[n1] = 99; we won't be needing these as to mark the end we already know the size of arrays
// R[n2] = 99;
// i = 1;
// j = 1;
/*Prior to the first iteration k = p, so the subarray is empty.
Both L[i] and R[j] are the smallest elements of their arrays and have not
been copied back to A*/
for (k = p; k < r+1 && i < n1 && j<n2; k++) {
//i & j checks weather the array has completed or not
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
}
else {
A[k]=R[j];
j++;
}
}
// when one of the array is empty u can copy the rest of the array with out compairing
while(i<n1)
A[k++]=L[i++];
while(j<n2)
A[k++]=R[j++];
free(L);
free(R);
}
void MERGE_SORT(int A[], int p, int r)
{
//During first iteration p = 1 & r = 8
if (p < r) {
int q = (p + r) / 2;
MERGE_SORT(A, p, q);
MERGE_SORT(A, q + 1, r);
MERGE(A, p, q, r);
}
}
/* 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");
}`
first of all you were not passing the right parameters to the function.
then the concept of using infinity to indicate is not good as one can want to sort bigger number than that in that case you would have to increase infinity an alternative approach is given above.
Mean while i also solved the problem with your code here it was again with the array index were not rightly used check it out now its working:`
#include<stdlib.h>
#include<stdio.h>
void MERGE(A, p, q, r);
void printArray(Arr, size);
void MERGE_SORT(A, p, r);
int main(void)
{
int A[] = { 12, 11, 13, 5, 6, 7, 2, 9 };
int arr_size = sizeof(A) / sizeof(A[0]);
printf("Given array is \n");
printArray(A, arr_size);
MERGE_SORT(A, 1, arr_size);
printf("\nSorted array is \n");
printArray(A, arr_size);
return 0;
}
void MERGE(int A[], int p, int q, int r)
{
int i = 0;
int j = 0;
int n1 = q - p + 1; //Computing length of sub-array 1
int n2 = r - q; //Computing length of sub-array 2
int *L = malloc((n1+1) * sizeof(*L+1)); //Creating Left array
int *R = malloc((n2+1) * sizeof(*R+1)); //Creating Right array
for (int i = 1; i < n1; i++) {
L[i] = A[p + i - 1];
}
for (int j = 1; j < n2; j++) {
R[j] = A[q + j];
}
L[n1] = 99; //Placing Sentinel at the end of array
R[n2] = 99;
i = 1;
j = 1;
/*Prior to the first iteration k = p, so the subarray is empty.
Both L[i] and R[j] are the smallest elements of their arrays and have not
been copied back to A*/
for (int k = p; k < r; k++) {
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
}
else if (A[k] == L[i])
j++;
}
free(L);
free(R);
}
void MERGE_SORT(int A[], int p, int r)
{
//During first iteration p = 1 & r = 8
if (p < r) {
int q = (p + r) / 2;
MERGE_SORT(A, p, q);
MERGE_SORT(A, q + 1, r);
MERGE(A, p, q, r);
}
}
/* 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");
}
I am trying to implement the merge sort algorithm in C. I understand how the algorithm is supposed to work however I am encountering some difficulties with the implementation.
I understand that there are hundreds of examples and source code for it's implementation but I was hoping someone could help me understand why mine is not working correctly.
My code is below and after the code I explain what I have tried so far.
#include <stdio.h>
void merge(int a[], int L[], int R[],int nL, int nR) //nL and nR are the lengths of L[] and R[]
{
int i = 0 , j = 0, k = 0;
while(i<nL && j <nR)
{
if(L[i] <= R[j]){
a[k] = L[i];
i++;
}
else{
a[k] = R[j];
j++;
}
k++;
}
while(i < nL){
a[k] = L[i];
i++;
k++;
}
while(j < nR) {
a[k] = R[j];
j++;
k++;
}
}
void mergesort(int a[],int n) //n is the length of a[]
{
if(n < 2) return; //BASE CASE
int mid = n / 2;
int left[mid];
int right[n-mid];
for(int i = 0; i < mid; i++)
{
left[i] = a[i];
}
for(int i = mid; i < n-1; i++)
{
right[i-mid] = a[i];
}
int nL = sizeof(left) / sizeof(left[0]);
int nR = sizeof(right) / sizeof(right[0]);
mergesort(left, nL);
mergesort(right, nR);
merge(a,left,right,nL,nR);
}
int main(void)
{
printf("Initial:\n");
printf("3 4 1 6\n");
int numbers[4] = {3,4,1,6};
int n = sizeof(numbers) / sizeof(int);
mergesort(numbers,n);
printf("Sorted:\n");
for(int i =0 ; i < 4; i++)
{
printf("%d ", numbers[i]);
}
return 0;
}
As it is and with the unsorted array [3,4,1,6] the output is 0 0 1 3.
Clearly the 1 and 3 are in the right order relative to each other but the two zeros at the beginning are clearly wrong. At first it seemed to me that I was inserting 4 and 6 to the right and out of bounds of the array.
I used some print statements to try and debug but I haven't been able to figure out what was going on. I even tried to follow my code with gdb but I still could not sort it.
Does any one have any ideas of what might be happening?
A more nearly idiomatic way of writing the merge() code would be:
void merge(int a[], int L[], int R[],int nL, int nR)
{
int i = 0, j = 0, k = 0;
while (i < nL && j < nR)
{
if (L[i] <= R[j])
a[k++] = L[i++];
else
a[k++] = R[j++];
}
while (i < nL)
a[k++] = L[i++];
while (j < nR)
a[k++] = R[j++];
}
That's about half the number of lines of your code, and within broad limits, the less code there is to read, the better. There are those who insist on having braces after each loop or conditional. I don't think that's necessary (or particularly helpful), but if that's the style you like, you can use it.
Your mergesort() code is less flabby, but could be changed to:
void mergesort(int a[],int n) //n is the length of a[]
{
if (n < 2)
return; //BASE CASE
int mid = n / 2;
int left[mid];
int right[n-mid];
for (int i = 0; i < mid; i++)
left[i] = a[i];
for (int i = mid; i < n; i++)
right[i-mid] = a[i];
mergesort(left, mid);
mergesort(right, n - mid);
merge(a, left, right, mid, n - mid);
}
This includes the fix for your main problem — the loop loading the right array was leaving the last element uncopied.
With a debugging function such as:
void dump_array(const char *tag, int n, int *a)
{
printf("%s:%d:", tag, n);
for (int i = 0; i < n; i++)
printf(" %3d", a[i]);
putchar('\n');
}
You can do a lot of effective debugging with:
void mergesort(int a[],int n)
{
if (n < 2)
return;
int mid = n / 2;
int left[mid];
int right[n-mid];
dump_array("-->>mergesort()", n, a);
for (int i = 0; i < mid; i++)
left[i] = a[i];
dump_array("left", mid, left);
for (int i = mid; i < n; i++)
right[i-mid] = a[i];
dump_array("right", n - mid, right);
mergesort(left, mid);
dump_array("merged-L", mid, left);
mergesort(right, n - mid);
dump_array("merged-R", n - mid, right);
merge(a, left, right, mid, n - mid);
dump_array("<<--mergesort()", n, a);
}
In your code, the output with the tag right would show 0 or semi-random data for the last element, rather than what you're expecting. This would be a hint as to where the trouble is. Keep the dump_array() function around; it is a useful creature to have. It's a simple-minded version; you can invent more complex versions which outputs a newline at intermediate positions for long arrays, for example.
The issue is in the following code:
for(int i = mid; i < n-1; i++)
{
right[i-mid] = a[i];
}
It should be:
for(int i = mid; i < n; i++) // right should range from mid to n - 1 *inclusive*
{
right[i-mid] = a[i];
}
This is simple implementation of merge sort without any complications. Just pass the array pointer and total number of entires in the array.
void merge(int *a, int top)// Array pointer and max entries
{
int l1, k, l2, u1, u2, size = 1, i, j;
int *sa;
sa = (int *)calloc(top, sizeof(int));
while (size < top)
{
l1 = 0;
k = 0;
while (l1 + size < top)
{
l2 = l1 + size;
u1 = l2 - 1;
u2 = ((l2 + size - 1) < top ? l2 + size - 1 : top - 1);
for (i = l1, j = l2; i <= u1 && j <= u2; )// Merging
{
sa[k++] = a[i] <= a[j] ? a[i++] : a[j++];
}
for ( ; i <= u1; )
sa[k++] = a[i++];
for ( ; j <= u2; )
sa[k++] = a[j++];
l1 = u2 + 1;
}
for (i = l1; i < top; i++) // For the left outs of the process
sa[k++] = a[i];
for (i = 0; i < top; i++)
a[i] = sa[i];
size *= 2;
}
}