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++];
}
}
}
Related
I'm stuck with trying to get the number of searches done within a Binary search algorithm.
The goal is to test how many searches are done depending on how much data is put into the algorithm.
The program in question
//CBinarysearch.c//
#include <stdio.h>
#include <time.h>
#define NUM 100
#define MAX 200
int binary_s(int a[], int n, int s) {
int lo, hi, mid;
int c = 0;
lo = 0;//loの初期化
hi = n-1;//hiの初期化
while (lo <= hi) {
mid = (lo + hi) / 2;//midの初期化
c++;
if (s == a[mid]) break;//探索値がmidと同じ値となればloopを終了
if (s > a[mid])//探索値がmidより大きい場合
lo = mid + 1;//loの値を;1してmidへ移動
else//探索値がmidより小さい場合
hi = mid - 1;//hiの値をー1してmidへ移動
}
if (lo <= hi)
printf("The numerical value %d is in array %d (array element %d)\n", s, mid+1, mid);
else
printf("Could not be located.\n");
return c;
}
void shuffle(int a[]) {
unsigned int i, j;
int tmp;
i = MAX - 1;
while (i > 0) {//シャッフルのためのLoop
j = rand() % (i + 1);//jの値をランダム化
tmp = a[j];
a[j] = a[i];
a[i] = tmp;
i--;
}
}
int quicksort(int a[], int first, int last) {
int i, j, temp, x;
i = first;
j = last;
x = (a[i] + a[j]) / 2;//基準値は平均
while (1) {
while (a[i] < x) i++;
while (a[j] > x) j--;
//iがjより大きくなればwhile loopが解除される
if (i >= j) break;
//a[i]とa[j]を入れ替える
temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
if (first < i-1) quicksort(a, first, i-1);
if (j + 1 < last) quicksort(a, j + 1, last);
return 0;
}
int main(void) {
int a[NUM];
int i;
int count;
int s;
srand((unsigned int)time(NULL));
i = rand() % NUM;
s = a[i];
for (i = 0; i < NUM; i++) {//整列数列の作成
a[i] = i + 1;
}
shuffle(a);//Fisher-Yates shuffle
quicksort(a, 0, NUM-1);//クイックソートの呼び出し
count = binary_s(a, NUM, s);
printf("\n%d ", count);//交換回数の出力
return 0;
}
I've been at this for an embarrassingly long time. And at this point I am adding more details just to make this post viable. It's been rough.
May I ask for some help, please?
You intialize s as s = a[i]; before initializing the array: this has undefined behavior. You should instead write:
s = rand() % NUM + 1;
Furthermore the shuffle function assumes the array has MAX elements whereas you define it with a length of NUM in main(). You should pass the length to shuffle().
Also note that x = (a[i] + a[j]) / 2 would have undefined behavior if the values in the array can be arbitrary large.
You should also consider adding some white space between the code and the comments to make the code more readable, especially to non Japanese readers.
Here is a modified version:
//CBinarysearch.c//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 100
int binary_s(int a[], int n, int s) {
int lo, hi, mid;
int c = 0;
lo = 0; // loの初期化
hi = n - 1; // hiの初期化
while (lo <= hi) {
mid = lo + (hi - lo) / 2; // midの初期化
c++;
if (s == a[mid]) // 探索€¤がmidと同じ€¤となればloopを終了
break;
if (s > a[mid]) // 探索€¤がmidより大きい場合
lo = mid + 1; // loの€¤を;1してmidへ移動
else // 探索€¤がmidより小さい場合
hi = mid - 1; // hiの€¤をー1してmidへ移動
}
if (lo <= hi) {
printf("The numerical value %d is in array at index %d\n",
s, lo);
} else {
printf("value %d Could not be located in array.\n", s);
}
return c;
}
void shuffle(int a[], int len) {
int i, j;
int tmp;
for (i = len - 1; i > 0; i--) { // シャッフルのためのLoop
j = rand() % (i + 1); // jの€¤をラン€ム化
tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
void quicksort(int a[], int first, int last) {
int i, j, temp, x;
if (first >= last)
return;
i = first;
j = last;
x = ((long long)a[i] + a[j]) / 2; // 基準€¤は平均
while (1) {
while (a[i] < x) i++;
while (a[j] > x) j--;
//iがjより大きくなればwhile loopが解除される
if (i >= j) break;
//a[i]とa[j]を入れ替える
temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
quicksort(a, first, i - 1);
quicksort(a, j + 1, last);
}
int main(void) {
int a[NUM];
int i;
int count;
int s;
srand((unsigned int)time(NULL));
for (i = 0; i < NUM; i++) { // 整列数列の作成
a[i] = i + 1;
}
s = a[rand() % NUM];
shuffle(a, NUM); // Fisher-Yates shuffle
quicksort(a, 0, NUM - 1); // クイックソートの呼び出し
count = binary_s(a, NUM, s);
printf("iterations: %d\n", count); // 交換回数の出力
return 0;
}
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'm getting an error Segmentation Fault:11, please help.
variable info:(s:start, e:end, m:mid, n:array), testing for a sample array n[] = {4,3,2,1}. a1 and a2 are temporary arrays. I am guessing there's something with the calculation of m:mid and passing it.
#include <stdio.h>
void merge(int s, int e, int m, int n[]) {
int l1 = m - s;
int l2 = e - m + 1;
int a1[l1];
int a2[l2];
for (int i = 0; i < l1; i++) {
a1[i] = n[s + i];
}
for (int j = 0; j < l2; j++) {
a2[j] = n[s + m + j];
}
int i = 0, j = 0;
for (int k = 0; k < l1 + l2; k++) {
if (a1[i] <= a2[j] && i != l1 && j != l2) {
n[k] = a1[i];
i++;
} else if (a2[j] <= a1[i] && i != l1 && j != l2) {
n[k] = a2[j];
j++;
} else if (j == l2 && i != l1) {
n[k] = a1[i];
i++;
} else if(i == l1 && j != l2) {
n[k] = a2[j];
j++;
}
}
}
void mergeSort(int s, int e, int n[]) {
if (s < e) {
int m = (e - s) / 2;
mergeSort(s, m - 1, n);
mergeSort(m, e, n);
merge(s, e, m, n);
}
}
int main(void) {
int n[] = { 4, 3, 2, 1 };
int r = 4;
mergeSort(0, r - 1, n);
for(int i = 0; i < r; i++) {
printf("%i\n", n[i]);
}
}
The computation of m for the middle element is bogus: you get the offset of m from s, not its index into the array.
Here is a corrected version:
void mergeSort(int s, int e, int n[]) {
if (s < e) {
int m = s + (e - s + 1) / 2;
mergeSort(s, m - 1, n);
mergeSort(m, e, n);
merge(s, e, m, n);
}
}
There are other issues in your code, notably:
you should check the offsets i and j beforedereferencinga1[i]anda2[j]`.
the offset k should not be used directly in the merge phase, you should store to n[s + k].
in the initialization loop for a2, you should use a2[j] = n[m + j]; instead of a2[j] = n[s + m + j];
Note also that it is idiomatic to pass ranges in C with the first index included and the last index excluded. This allows for passing empty ranges, which your current method does not. It also makes the code much simpler and easier to read.
Here is a modified version:
#include <stdio.h>
void merge(int s, int e, int m, int n[]) {
int l1 = m - s;
int l2 = e - m;
int a1[l1];
int a2[l2];
for (int i = 0; i < l1; i++) {
a1[i] = n[s + i];
}
for (int j = 0; j < l2; j++) {
a2[j] = n[m + j];
}
for (int i = 0, j = 0, k = 0; k < l1 + l2; k++) {
if (i < l1 && (j >= l2 || a1[i] <= a2[j])) {
n[s + k] = a1[i];
i++;
} else {
n[s + k] = a2[j];
j++;
}
}
}
void mergeSort(int s, int e, int n[]) {
if (e > s + 1) {
int m = s + (e - s) / 2;
mergeSort(s, m, n);
mergeSort(m, e, n);
merge(s, e, m, n);
}
}
int main(void) {
int n[] = { 4, 3, 2, 1 };
int r = sizeof(n) / sizeof(n[0]);
mergeSort(0, r, n);
for(int i = 0; i < r; i++) {
printf("%i\n", n[i]);
}
return 0;
}
I've modified your code in several places. Try to use your debugger or pen & paper to understand what's going on under the hood.
void merge(int s, int e, int m, int n[]){
int l1 = m-s + 1;
int l2 = e - m;
int a1[l1];
int a2[l2];
for(int i = 0; i < l1; i++){
a1[i] = n[s+i];
}
for(int j = 0; j < l2; j++){
a2[j] = n[m+j + 1];
}
int i = 0, j = 0;
for(int k = 0; k < l1+l2; k++){
if(a1[i] <= a2[j] && i != l1 && j != l2){
n[k] = a1[i];
i++;
}else if(a2[j] <= a1[i] && i != l1 && j != l2){
n[k] = a2[j];
j++;
}else if(j == l2 && i != l1){
n[k] = a1[i];
i++;
}else if(i == l1 && j != l2){
n[k] = a2[j];
j++;
}
}
}
void mergeSort(int s, int e, int n[]){
if(s<e){
int m = s + (e-s)/2;
mergeSort(s, m, n);
mergeSort(m + 1, e, n);
merge(s,e,m, n);
}
You will be fine I guess.
I think you have a stack overflow problem because of infinite recursive calls. Look
void mergeSort(int s, int e, int n[]){
if(s<e){
int m = (e-s)/2;
mergeSort(s, m-1, n);
mergeSort(m, e, n);
merge(s,e,m, n);
}
}
You pass these values of s and e:
s e function
-------------
0 3 mergeSort
0 0 mergeSort -> end
1 3 mergeSort
0 0 mergeSort -> end
1 3 mergeSort
... (infinite calls)
Then the stack grows and grows while new functions called until in the end it exceeds the maximum possible size, which leads to SEGFAULT.
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;
}
}
I am having something that troubles me. I have my implementation of a quick sort algorithm, but when I test it on an array of integers that has over 30 elements, sorting takes, in my opinion to long. Sometimes even more than 10 seconds, unlike with selection sort, insertion sort and bubble sort, which are faster on 10000 elements than quick sort on 100 elements.
Here is my solution, please give advice :)
void kvikSort(int a[], int l, int d) {
int i, k;
if (l >= d)
return;
k = l;
swap(&a[l], &a[(l + d) / 2]);
for (i = l + 1; i <= d; i++)
if (a[i] < a[l])
swap(&a[++k], &a[i]);
swap(&a[l], &a[k]);
kvikSort(a, 0, k-1);
kvikSort(a, k+1, d);
}
EDIT: I am using GCC v 4.7.2 on my Linux Mint 14, proc: intel core2duo e7400
EDIT: My other algorithms:
void selectionSort(int a[], int n) {
int i, j, min;
for (i = 0; i < n - 1; i++) {
min = i;
for (j = i + 1; j < n; j++)
if (a[j] < a[min])
min = j;
if (min != i)
swap(&a[min], &a[i]);
}
}
void insertionSort(int a[], int n) {
int i, j;
for (i = 0; i < n - 1; i++)
for (j = i + 1; j > 0 && a[j] < a[j-1]; j--)
swap(&a[j], &a[j-1]);
}
void bubbleSort(int a[], int n) {
int i, j;
for (i = n - 1; i > 0; i--)
for (j = 0; j < i; j++)
if (a[j] > a[j+1])
swap(&a[j], &a[j+1]);
}
void swap(int *i, int *j) {
int tmp;
tmp = *i;
*i = *j;
*j = tmp;
}
EDIT: Maybe I should mention that in my test program I am first outputing randomly generated array to a text file, then sorted array to another text file. So it is certainly running slow, but that's not the problem, the problem is that quick sort runs a lot slower than the rest.
Your first recursive call
kvikSort(a, 0, k-1);
has the wrong lower bound, it should be
kvikSort(a, l, k-1);
With a lower bound of 0, you re-sort the initial part of the array again and again.
Here's the problem:
void kvikSort(int a[], int l, int d) {
int i, k;
if (l >= d)
return;
k = l;
swap(&a[l], &a[(l + d) / 2]);
for (i = l + 1; i <= d; i++)
if (a[i] < a[l])
swap(&a[++k], &a[i]);
swap(&a[l], &a[k]);
>>> kvikSort(a, 0, k-1);
kvikSort(a, l, k-1);
kvikSort(a, k+1, d);