I recently tried to implement the merge sort algorithm. I understood the basic concept behind it and even took a peek at one of it's C implementations online but when I try to do it on my own, I always seem to get a segmentation fault.
I am also confused if to use mid or mid+1 in places.
Please help me fix this.
#include <stdio.h>
#include <math.h>
int merge(int arr[], int low, int mid, int high);
int mergeSort(int arr[], int low, int high);
int main(void)
{
int sample[5]={66,7,11,2,99}; //Sample array for sorting.
mergeSort(sample, 0, 4); //Calling the function
}
int merge(int arr[], int low, int mid, int high)
{
if(high>low) //Merge will only work when high is greater than low and mid.
{
int leftSide[mid]; // Dividing the array into two parts, this is the left side; low to mid.
int rightSide[(high-mid)]; //This is the right side; mid to high.
for(int i=0;i<=mid;i++)
{
leftSide[i]=arr[i]; //Filling the leftSide array.
}
for (int x=mid; x<=high; x++)
{
rightSide[x]=arr[x]; //Filling the rightSide array.
}
for(int m,l,r=0; m<=high; m++)
{
if(leftSide[l]>rightSide[r])
{
arr[m]=rightSide[r]; //If the element on rightSide is lesser than on the leftSide then it will come first in the final array.
r++; //Increment the counter so next comparision can begin.
}
else if(leftSide[l]<rightSide[r])
{
arr[m]=leftSide[l]; //If the element on leftSide is lesser than on the rightSide then it will come first in the final array
l++; //Increment the counter so next comparision can begin.
}
else //This will be the case if the numbers are equal
{
if(l<mid) //If the left Index has not reached its maximum limit
{
arr[m]=leftSide[l];
l++;
}
else if(r<(high-mid)) // If the right Index has not reached its maximum limit
{
arr[m]=rightSide[r];
r++;
}
}
}
return 0;
}
else
{
return 1;
}
}
int mergeSort(int arr[], int low, int high)
{
if(high>low)
{
int mid=round((high+low)/2);
mergeSort(arr, low, mid);
mergeSort(arr, mid, high);
merge(arr, low, mid, high);
}
else //Base Case
{
return 1;
}
for(int i=0; i<=high;i++) //Printing the array.
{
printf("%d",arr[i]);
}
return 1;
}
Segmentation fault occur when you access unallocated memory.
In mergeSort function, you called merge function by passing indexes to call merge function.
if(high>low)
{
int mid=round((high+low)/2);
mergeSort(arr, low, mid);
mergeSort(arr, mid, high);
merge(arr, low, mid, high);
}
Inside merge function when you declared leftSide and rightSide arrays, The size of arrays should be index + 1, As array index starts from 0.
int leftSide[mid + 1]; // Dividing the array into two parts, this is the left side; low to mid.
int rightSide[(high-mid) + 1]; //This is the right side; mid to high.
In for(int m,l,r=0; m<=high; m++) the variables m and l are uninitialized. You only set r to 0. This means you will potentially index into arr[] and leftSide[] with illegal indices, causing a segfault.
The variables m, l, r are so called automatic variables, and these are default uninitialized.
Also, when you split arr[] between leftSide[] and rightSide[], you are placing the arr[mid] value into both arrays.
Related
In code of quicksort algorithm there is and usage of recursion without escape sequence, but the recursion stops and code finishes till end.I have found many code examples of this algorithm, but with escape sequence and this doesnt have one and still works.
I have tried to write everything on paper, but cant find why recursion stops, so function recursively continues forever,
/* C implementation QuickSort */
#include<stdio.h>
// A utility function to swap two elements
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
/* This function takes last element as pivot, places
the pivot element at its correct position in sorted
array, and places all smaller (smaller than pivot)
to left of pivot and all greater elements to right
of pivot */
int partition (int arr[], int low, int high)
{
int pivot = arr[high]; // pivot
int i = (low - 1); // Index of smaller element
for (int j = low; j <= high- 1; j++)
{
// If current element is smaller than the pivot
if (arr[j] < pivot)
{
i++; // increment index of smaller element
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
/* The main function that implements QuickSort
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void quickSort(int arr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[p] is now
at right place */
int pi = partition(arr, low, high);
// Separately sort elements before
// partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
/* 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");
}
// Driver program to test above functions
int main()
{
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: n");
printArray(arr, n);
return 0;
}
In my point of view function would be stucked on
quickSort(arr, low, pi - 1);
continues even when parameter at position pi - 1 reaches 0
but it obviously doesnt.
and arr ends up sorted in 1 5 7 8 9 10
Your base case is when if (low < high) is false, by Eugene Sh.
Has this function been written correctly?
It seems that something is wrong when I try to run the function with a large number of elements in the array (eg 1000).
Then its appears to stop.
int quick_sort(int n, int tablica[],int b, int a)
{
if(a==n-1 || n==0) return;
if(b==n-1)
{
b=0;
a++;
}
if(tablica[b]>tablica[b+1])
{
bufor=tablica[b];
tablica[b]=tablica[b+1];
tablica[b+1]=bufor;
}
b++;
return quick_sort(n,tablica,b,a);
}
Above code will not work even for a small array, unless the small array is unsorted in a particular way. It compares one element with the next element. If the array is say {4,3,8,7,1} the sort will fail, because it has no mechanism to push 1 to the start of the array.
For larger arrays there are too many recursion and the program hits the stack limit and simply fails.
You can use recursion in quicksort but the number of recursions has to be kept in check. For example for array of size 1000 you don't want to have to more than 1000 recursion. Example:
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
void quicksort(int arr[], int low, int high)
{
if(low < high)
{
int pivot = arr[high];
int i = (low - 1);
for(int j = low; j <= high - 1; j++)
{
if(arr[j] <= pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
int pi = i + 1;
quicksort(arr, low, pi - 1);
quicksort(arr, pi + 1, high);
}
}
int main()
{
int arr[] = { 7,3,6,1,4,8,9,2 };
int arrsize = sizeof(arr) / sizeof(*arr);
quicksort(arr, 0, arrsize - 1);
for(int i = 0; i < arrsize; i++)
printf("%d\n", arr[i]);
return 0;
}
I'm written a quick sort implementation in C. Changing the rand function range(using the remainder) in the first loop changes the running time of the algorithm dramatically. As it is right now, the algorithm takes 43 seconds. Changing the range from 100 to 10000 reduces the running to 0.9 seconds.
Why is that?
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
void quick_sort(int array[], int low, int high);
int partition(int array[], int low, int high);
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main(void)
{
const int len = 1000000;
srand(time(NULL));
int array[len];
puts("Populating the array...\n");
for(int i = 0; i < len; i++)
array[i] = rand() % 100; // Changing this line dramatically reduce the running time
puts("|Now sorting the array...|\n");
quick_sort(array, 0, len-1);
/*for(int i = 0; i < len; i++)*/
/*printf("%d ", array[i]);*/
}
void quick_sort(int array[], int low, int high)
{
int j;
if(low < high)
{
j = partition(array, low, high);
quick_sort(array, low, j-1);
quick_sort(array, j+1, high);
}
}
int partition(int array[], int low, int high)
{
int pivot = array[high];
int leftwall = low-1;
for(int i = low; i < high; i++)
{
if(array[i] <= pivot)
{
++leftwall;
swap(&array[leftwall], &array[i]);
}
}
swap(&array[leftwall+1], &array[high]);
return ++leftwall;
}
My guess is that when partitioning the array you end up moving a large number of duplicate values. When you pick the random numbers from only 100 choices, the array of a million elements will have about 10,000 of each value. It looks like you'll be swapping them around every call to partition due to the array[i] <= pivot comparison. For example, when you are almost done and a partition has only two distinct values in it, it still has about 20,000 elements…
I am trying to write code for homework in C that will take 10 integers from user input into an array and sort it using a recursive merge sort. We have not gone over pointers yet so I wanted to avoid using that in my code (many online examples use pointers).
Here is my code:
/* This code will take input for 10 integers given by the user
into an array, sort them with a recursive merge function
and print the updated array in ascending order. */
#include <stdio.h>
#define ARRSIZE 10
void merge_sort (int arr[], int temp[], int left, int right);
void merge (int arr[], int temp[], int left, int mid, int right);
int main (void){
int arr[ARRSIZE], temp[ARRSIZE], left, right, i;
printf("Enter 10 integers for an array:");
for(i=0;i<ARRSIZE;i++){
printf("\narray value %d:", i+1);
scanf("%d", &arr[i]);
}
left = 0;
right = ARRSIZE-1;
merge_sort(arr, temp, left, right);
printf("\nHere is your updated array:");
printf("\n{");
for(i=0;i<ARRSIZE;i++){
printf("%d,", arr[i]);
}
printf("}");
return 0;
}
void merge_sort (int arr[], int temp[], int left, int right){
if(left<right){
int mid = (right-left)/2;
merge_sort(arr, temp, left, mid);
merge_sort(arr, temp, mid+1, right);
merge(arr, temp, left, mid, right);
}
}
void merge (int arr[], int temp[], int left, int mid, int right){
int i, j, tempi = 0;
for(i=left, j=mid+1; i<=mid && j<=right ;){
// mid+1 is the start of the right array
if(arr[i]<arr[j] && i<=mid){
temp[tempi] = arr[i];
tempi++;
i++;
}
else if(arr[i]>arr[j] && j<=right){
temp[tempi] = arr[j];
tempi++;
j++;
}
}
for(i=0,j=right; i<=j; i++){
arr[i] = temp[i];
}
}
I keep getting a segmentation fault when I run this in my linux shell. Any suggestions?
Well, I've found one subtle bug already: int mid = (right-left)/2; should be int mid = (left+right)/2;
Also, what flows I've found:
You should use tempi = left for simplicity. Simply copy incoming part of array to corresponding part of temporary array.
In your merge cycle, you can place incrementing tempi inside for definition:
for( ...; ...; ++tempi)
Inside that loop you check boundaries AFTER you have read values from that place. This is very bad. VERY. Although, You haven't encounter any problems here just because you are checking boundaries inside for definition :) simply remove them:
for (i = left, j = 1 + mid; i <= mid && j <= right; ++tempi)
{
if (arr[i] < arr[j]) temp[tempi] = arr[i++];
else /* arr[j] <= arr[i] */ temp[tempi] = arr[j++];
}
Cause this loop exits when either subarray has reached end, you have to copy rest of items from another subarray to temp[]:
if (i > mid) i = j; /* if we need to copy right subarray */
for (; tempi <= right; ++tempi, ++i) temp[tempi] = arr[i];
So, your copying back from temporary array will look like
for (i = left; i <= right; ++i) arr[i] = temp[i];
i wrote this code in C language on Xcode following the algorithm of mergesort.
The problem is that sometimes i get EXC_BAD_ACCESS and i can't manage where the error is!
The merge algorithm should work (i tried it outside the mergesort function and works!). Thank you for your help and patience!
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DIM 6
void mymerge (int v[], int i1,int i2, int last); //mergesort core: merge two ordinated arrays in one bigger ordinated array
void mymergesort (int v[], int lower, int upper);//mergesort
void printv (int v[],int lower, int upper);
int main () {
int i;
srand((unsigned int)time(NULL));
int v[DIM];
for (i=0; i<DIM; i++)
v[i]=rand()%15;
printv(v, 0, DIM-1);
getc(stdin);
mymergesort(v, 0, DIM-1);
printv(v, 0, DIM-1);
}
void printv (int v[],int lower, int upper){
int i;
for (i=lower; i<=upper; i++)
printf("%d\t",v[i]);
}
void mymergesort (int v[], int lower, int upper){
int mid=(upper+lower)/2;
if (upper<lower) {
mymergesort(v, lower, mid);
mymergesort(v, mid+1, upper);
mymerge(v,lower,mid+1,upper);
}
}
void mymerge (int v[], int i1,int i2, int last){
int i=i1,j=i2,k=i1,*vout;
vout=(int*)malloc((last-i1+1)*sizeof(int));
while (i<i2 && j<=last) {
if (v[i]<=v[j]) {
vout[k++]=v[i++];
}else {
vout[k++]=v[j++];
}
}
for (;i<i2;i++) vout[k++]=v[i];
for (;j<=last;j++) vout[k++]=v[j];
for (k=i1; k<=last; k++) v[k]=vout[k];
free(vout);
}
EDIT:
thank you very much! but i think think there is another problem, when I try to sort a bigger array (200 elements), the program doesn't work (i get a malloc error: incorrect checksum for freed object - object was probably modified after being freed). But if I run it from the xCode debugger everything works fine
This: vout=(int*)malloc((last-i1)*sizeof(int)); is wrong.
First, the number of elements you want is last-i1+1, not last-i1 - classic off-by-1. This kind of error is one of the reasons why the convention in C code is to make lower bounds inclusive and upper bounds exclusive - less +1 and -1 you need to do, less opportunity to screw up.
The more serious error is that you index vout starting from i1. If you do it this way, you need to allocate last+1 element for vout, and you never use the first i1 (index 0 .. i1-1).
Fix: First, allocate last-i1+1 elements. Second, initialize k to 0 at the beginning, not i1. Third, change the final copy to be
for (k=i1; k<=last; k++) v[k] = vout[k-i1];
You have two problems. The first is that your calculation of the midpoint is incorrect - you use (upper - lower)/ 2, but this is not guaranteed to lie between lower and upper. What you actually want is lower + (upper - lower) / 2. It's also not necessary to do any work if there's only 1 number in the interval to be sorted - so the mymergesort() function should look like:
void mymergesort (int v[], int lower, int upper)
{
if (upper > lower) {
int mid = lower + (upper - lower)/2;
mymergesort(v, lower, mid);
mymergesort(v, mid+1, upper);
mymerge(v,lower,mid+1,upper);
}
}
The second problem is the one in the mymerge() function already pointed out by Fabian Giesen.
#include<stdio.h>
#include<stdlib.h>
void merge(int *a, int n1, int *b, int n2, int *arr)
{
int i=0, j=0, n=0;
while(i<n1 && j<n2)
{
if (a[i] < b[j])
{
arr[n++] = a[i];
i++;
}
else
{
arr[n++] = b[j];
j++;
}
}
while( i < n1)
arr[n++] = a[i++];
while( j < n2)
arr[n++] = b[j++];
}
void merge_sort(int *a, int n)
{
int left[n/2], right[n-n/2],i=0;
if (n<=1)
return ;
while(i<n/2)
left[i] = a[i++];
while(i<n)
right[i - n/2] = a[i++];
merge_sort( left, n/2 );
merge_sort( right, n-n/2);
merge(left, n/2, right, n-n/2, a);
}
void main()
{
int a[] = { 6, 5, 3, 1,9, 8, 7, 2, 4},i;
merge_sort(a,sizeof(a)/sizeof(a[0]));
for(i=0;i<9;i++)
printf("--%d",a[i]);
printf("\n");
}
-- s.k
#include<stdio.h>
#include<conio.h>
#define max 20
/*** function for merging the adjecent subarrays in sorted order ***/
void merge(int A[max],int n,int low,int high, int mid)
{
int i=low,j=mid+1,k,temp;
while((i<=j)&&(j<=high))
{
if(A[i]>A[j]) /** if element of the second half is greater then exchg and shift **/
{
temp=A[j];
for(k=j;k>i;k--) /** shifting the elements **/
{
A[k]=A[k-1];
}
A[i]=temp;
j++;
}
i++;
}
}
/******* iterative function for merge sort ********/
void merge_sort(int A[max],int n,int low,int high)
{
int mid;
if(low<high) /** terminating condition **/
{
mid=(high+low)/2; /** calculating the mid point ***/
merge_sort(A,n,low,mid); /*** recursive call for left half of the array ***/
merge_sort(A,n,mid+1,high); /*** recursive call for right half of the array ***/
merge(A,n,low,high,mid); /** merging the both parts of the array **/
}
}
/******* begening of the main function **********/
int main()
{
int A[max],n,i;
/** reading the inputs fro users **/
printf("\n enter the size of the array\n");
scanf("%d",&n);
printf("\n enter the array \n");
for(i=0;i<n;i++)
{
scanf("%d",&A[i]);
}
/*** calling merge sort ***/
merge_sort(A,n,0,n-1);
/** printing the sorted array **/
for(i=0;i<10;i++)
{
printf("\n\t%d",A[i]);
}
getch();
return 0;
}