Why is this program giving a stack overflow? - c

My C merge code works when I initialize the array globally at the top of the program until the stack overflows. I'm trying to initialize the array with malloc, but when I do, the code will only read in two integers and stop running.
This program pulls random numbers from a file called alg.txt and then sorts them. Again, the code works (up until 500k integers) when defining z at the top of the program to the number of integers to be sorted, and declaring the array globally to be equal to arr[z]. How do I figure out what is going on?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int count = 0;
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];
count++;
for (j = 0; j < n2; j++)
R[j] = arr[m + 1+ j];
count++;
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2)
{
if (L[i] <= R[j])
{
arr[k] = L[i];
i++;
count++;
}
else
{
arr[k] = R[j];
j++;
count++;
}
k++;
count++;
}
while (i < n1)
{
arr[k] = L[i];
i++;
k++;
count++;
}
while (j < n2)
{
arr[k] = R[j];
j++;
k++;
count++;
}
}
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);
}
}
void printArray(int A[], int size)
{
int i;
for (i=0; i < size; i++)
printf("%d ", A[i]);
printf("\n");
}
int main()
{
int i;
FILE *myFile;
myFile = fopen("alg.txt", "r");
int z;
printf("Enter the size of the array: ");
scanf("%d", &z);
int *arr = (int *)malloc(z*sizeof(int));
int n = sizeof(arr)/sizeof(arr[0]);
for(i=0; i < z; i++)
{
fscanf(myFile, "%d,", &arr[i]);
}
mergeSort(arr, 0, n - 1);
printf("\nSorted array is \n");
printArray(arr, n);
printf("count is %d\n", count);
return 0;
}

You are declaring the L and R array's locally (inside the merge function). They consume stack on every function call. You should initialize them globally so that they can be stored on the heap. Also, you should declare just one array (like temp_arr in the below code) as there's no need for two separate arrays like L and R.
See Local vs Global variable storage
You can improve your merge function to this:
int temp_arr[500000]; // Global declaration
void merge(int arr[], int l, int m, int r)
{
int i=l, j=m+1, k=l;
while(i<=m && j<=r)
{
if(arr[i]<arr[j])
temp_arr[++k] = arr[++i];
else temp_arr[++k] = arr[++j];
}
while(i<=m)
temp_arr[++k] = arr[++i];
while(j<=r)
temp_arr[++k] = arr[++j];
i = l;
while(i<=r)
arr[i] = temp_arr[++i]; // Storing stored array in arr
}

Related

How to do merge sort without using additional arrays for splitting the initial array?

I was trying to solve a problem which asks to write merge sort code but without using additional arrays for partitioning the initial array. I guess the code is wrote is almost good but the problem I am facing is that I can't figure out how to maintain and update the array while being sorted. I know the problem is in Merge function.
How can I fix the code?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void PrintArray(int A[], int n)
{
for(int i=0; i < n; i++)
printf("%d ", A[i]);
printf("\n");
}
void merge(int A[], int left, int mid, int right, int n){
int B[n];
int i = left, j = mid+1, k=0;
while(i<=mid && j <= right){
if(A[i]>=A[j]){
B[k++] = A[i++];
}
else {
B[k++] = A[j++];
}
}
while(i<=mid){
B[k++] = A[i++];
}
while(j<=right){
B[k++] = A[j++];
}
for(i=0; i<n; i++){
A[i] = B[i];
}
}
void MergeSort(int A[], int left, int right, int n)
{
if(left<right){
int mid;
mid = floor((left+right)/2);
MergeSort(A,left,mid,n/2);
MergeSort(A,mid+1,right,n/2);
merge(A,left,mid,right,n);
}
else return;
}
int main()
{
int n;
scanf("%d",&n);
int A[n];
for(int i=0; i < n; i++) scanf("%d", &A[i]);
MergeSort(A, 0, n-1, n);
PrintArray(A, n);
return 0;
}
In the final for loop in merge, change:
A[i] = B[i];
Into:
A[left + i] = B[i];
Edit: Even after that fix, the sort was still wrong. The correct fix for the final loop is:
for (i = left; i <= right; ++i)
A[i] = B[i - left];
The original for (i = 0; i < n; ++i) didn't work because just passing n / 2 could pass a value that was one less than needed. With this new fix, n doesn't need to be passed to merge at all. So, n is really only needed for the public function. See the UPDATE section below.
Side notes:
You don't need to use floor at all. It's superfluous for integer math [and might make the results less accurate].
You are sorting in reverse order (e.g. 3, 2, 1 instead of 1, 2, 3). To sort in ascending order, in merge, change: if (A[i] >= A[j]) to if (A[i] <= A[j])
You are not creating an initial extra array, but you have B on the stack in merge, so, you are using an auxiliary/temp array. This is true regardless whether you copy from A to B at the start of merge or copy back from B to A at the end of merge
So, you don't have a true "in-place" algorithm.
In fact, for large enough arrays, having B on the stack would cause a stack overflow. It might be better to use a heap allocation for B.
You could put this in a global/public "wrapper" function for mergeSort (e.g. mergeSortPublic). Do (e.g.) B = malloc(sizeof(int) * n) at the start and do free(B) at the end. You can make B global scope or pass it as an extra arg to your merge functions
UPDATE:
Here's a fully cleaned up version that adds diagnostic tests.
Because of the change in the final loop in merge, it no longer needs the n value. So, it's no longer needed in mergeSort either with the mergeSortPub change.
I refactored the first loop in merge to be slightly faster by not refetching already fetched array values. The optimizer might have found this speedup, but I think it's better to state it explicitly.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void
PrintArray(int A[], int n)
{
int totlen = 0;
for (int i = 0; i < n; i++) {
totlen += printf(" %d", A[i]);
if (totlen >= 72) {
printf("\n");
totlen = 0;
}
}
if (totlen > 0)
printf("\n");
}
void
merge(int A[], int left, int mid, int right, int *B)
{
int i = left,
j = mid + 1,
k = 0;
int Ai = A[i];
int Aj = A[j];
while (i <= mid && j <= right) {
if (Ai <= Aj) {
B[k++] = Ai;
Ai = A[++i];
}
else {
B[k++] = Aj;
Aj = A[++j];
}
}
while (i <= mid)
B[k++] = A[i++];
while (j <= right)
B[k++] = A[j++];
// original code
#if 0
for (i = 0; i < n; i++)
A[i] = B[i];
#endif
// first fix -- still broken
#if 0
for (i = 0; i < n; i++)
A[left + i] = B[i];
#endif
// correct fix
#if 1
for (i = left; i <= right; ++i)
A[i] = B[i - left];
#endif
}
void
MergeSort(int A[], int left, int right, int *B)
{
if (left < right) {
int mid = (left + right) / 2;
MergeSort(A, left, mid, B);
MergeSort(A, mid + 1, right, B);
merge(A, left, mid, right, B);
}
}
void
MergeSortPub(int A[], int n)
{
int *B = malloc(sizeof(*B) * n);
MergeSort(A,0,n - 1,B);
free(B);
}
void
dotest(int tstno)
{
int n = rand() % 1000;
int *A = malloc(sizeof(*A) * n);
for (int i = 0; i < n; ++i)
A[i] = n - i;
MergeSortPub(A,n);
int old = A[0];
int bad = 0;
for (int i = 1; i < n; ++i) {
int cur = A[i];
if (cur < old) {
if (! bad)
printf("dotest: %d -- i=%d old=%d cur=%d\n",tstno,i,old,cur);
bad = 1;
}
old = cur;
}
if (bad) {
PrintArray(A,n);
exit(1);
}
}
int
main(void)
{
int n;
#if 0
scanf("%d", &n);
int A[n];
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
MergeSortPub(A, n);
PrintArray(A, n);
#else
for (int tstno = 1; tstno <= 1000; ++tstno)
dotest(tstno);
#endif
return 0;
}
There are a few variations of merge sort that do not use any additional space other than local variables. Optimal implementations of this are complicated and about 50% slower than conventional merge sort, and most of these implementations are for academic research.
There is a wiki article for one variation, that is hybrid of insertion and merge sort.
https://en.wikipedia.org/wiki/Block_sort
Link to a more optimized version in grailsort.h in this github repository. The void GrailSort(SORT_TYPE *arr,int Len) function does not use any additional buffer.
https://github.com/Mrrl/GrailSort

Weird behaviour when merge sorting an array

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
void DISPLAY(int*, int);
int* MERGE(int*, int, int* , int, int* , int );
int* MERGESORT(int* , int );
int main()
{
int* array = NULL;
array = (int*) malloc(7 * sizeof(int) );
array[0] = 10;
array[1] = 9;
array[2] = 8;
array[3] = 57;
array[4] = 6;
array[5] = 5;
array[6] = 24;
printf("\nOriginal array: \n");
DISPLAY(array, 7);
array = MERGESORT(array, 7);
printf("\n\nResulting array: \n");
DISPLAY(array, 7);
}
void DISPLAY(int* array, int size)
{
int i;
for(i = 0; i < size; i++){
printf("%d ", array[i]);
}
}
int* MERGESORT(int *array, int size) {
if(size < 2)
return array;
int mid = size / 2;
int rightsize = size-mid;
int* L = (int*) malloc(mid * sizeof(int));
int* R = (int*) malloc((size-mid) * sizeof(int));
int i;
for(i = 0; i < mid; i++)
L[i] = array[i];
for(i = mid; i < size; i++)
R[i-mid] = array[i];
L = MERGESORT(L, mid);
R = MERGESORT(R, size-mid);
array = MERGE(array, size, L, mid, R, rightsize);
free(L);
free(R);
return array;
}
int* MERGE(int *array, int size, int *L, int leftsize,int *R, int rightsize) {
int i, j, k;
i = 0;
j = 0;
k = 0;
while(i < leftsize && j < rightsize)
{
if(L[i] < R[j])
{
array[k] = L[i];
i++;
}
else{
array[k] = R[j];
j++;
}
k++;
}
while(i < leftsize)
{
array[k] = L[i];
i++;
k++;
}
while(j < rightsize)
{
array[k] = L[j];
j++;
k++;
}
printf("\nUpdated array: \n");
DISPLAY(array, size);
return array;
}
Hello, I have a problem when I try to perform merge sorting in an array. Some items in the array are printing weird. Every time the arrays get updated, the value gets printed out, but it doesn't work at specific inputs.
When I input small numbers (like 1-2 digits), then the array gets sorted normally.
Sample run of program sorting incorrectly:
https://i.stack.imgur.com/OLg0b.png
while(j < rightsize)
{
array[k] = L[j];
j++;
k++;
}
should be
while(j < rightsize)
{
array[k] = R[j];
j++;
k++;
}
Umm, sorry for wasting time on such a simple mistake.
Consider.
int* R = (int*) malloc((size-mid) * sizeof(int));
(which ought to be written int *R = malloc((size - mid) * sizeof *R);)
What is the highest valid index in that array? Trying to read from (or write to) R[n] for n > size - mid - 1 invokes undefined behavior.

Undesired behaviour in MergeSort function

I'm new to programming,I'm trying to implement a merge sort function into my program, but it's not working correctly. I went over and over the code but I can't find the problem.
If for example the user input for a 6 element array is : 3 2 4 1 6 7
The output is: 1 3 2 4 32708 32708
Can someone help me? Also, if anyone have any advice for improving my coding style would be much appreciated.Thanks.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a, n;
a = malloc(100 * sizeof(int)); // dynamically allocating memory for original array
if (a == NULL)
return 1;
printf("Enter n of elements in the array:");
scanf("%i", &n); // n of elements the in array
printf("Enter elements:\n");
for (int i = 0; i < n; i++) {
scanf("%i", &a[i]); // prompt user for input elements
}
int f, l, m, n1, n2; // declaring variables
f = 0; // first element
l = n - 1; // last element
m = (f + l) / 2; // mid point
n1 = m + 1 - f; // n elements l1
n2 = l - m; // n elements l2
int l1[n1]; // temp array 1
int l2[n2]; // temp array2
for (int i = 0; i < n1; i++) {
l1[i] = a[i]; // copy elements into temp l1
}
for (int j = 0; j < n2; j++) {
l2[j] = a[m + 1 + j]; // copy elements into temp l2
}
int i, j, k; // variable for arrays index
i = 0;
j = 0;
k = 0;
//sorting and copying elements in original array
while (i < n1 && j < n2) {
if (l1[i] <= l2[j]) { // if element l1 smaller or equal to l2 element
a[k] = l1[i]; // copy element l1 into original array
i++; // increment l1
} else { // if element l1 bigger than l2
a[k] = l2[j]; // copy element l2 into original array
j++; // increment l2
}
k++; // increment original array
}
// copy remaining elements (if any)
while (i < n1) {
a[k] = l1[i];
i++;
k++;
}
while (j < n2) {
a[k] = l2[i];
j++;
k++;
}
printf("Your sorted array:\n");
for (int d = 0; d < n; d++) {
printf("%i ", a[d]); // print sorted array
}
printf("\n");
free(a); // freeing original array
}
You need to merge recursively. You wrote only the merge part and not the recursive sort function.
More info: https://www.geeksforgeeks.org/merge-sort/
#include <stdio.h>
#include <stdlib.h>
void merge(int arr[], int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
/* create temp arrays */
int L[n1], R[n2];
/* Copy data to temp arrays L[] and R[] */
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
/* Merge the temp arrays back into arr[l..r]*/
i = 0; // Initial index of first subarray
j = 0; // Initial index of second subarray
k = l; // Initial index of merged subarray
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
/* Copy the remaining elements of L[], if there are any */
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
/* Copy the remaining elements of R[], if there are any */
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r)
{
if (l < r) {
// Same as (l+r)/2, but avoids overflow for
// large l and h
int m = l + (r - l) / 2;
// Sort first and second halves
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int main(void)
{
int *a, n;
printf("Enter n of elements in the array:");
scanf("%i", &n); //n of elements the in array
a = malloc(n * sizeof(int)); //dynamically allocating memory for original array
if (a == NULL)
return 1;
printf("Enter elements:\n");
for (int i = 0; i < n; i++) {
scanf("%i", &a[i]); //prompt user for input elements
}
mergeSort(a, 0, n - 1);
printf("Your sorted array:\n");
for (int d = 0; d < n; d++) {
printf("%i ", a[d]); //print sorted array
}
printf("\n");
free(a); //freeing original array
return 0;
}

Heapsort program does not return the result

I am implementing the Heapsort program in C language which sorts with the binary tree. When I run this program, it is ok until it meets the heapsort function in the program. I also try to debug this but it still gets wrong as meeting the heapsort function.
As referencing some algorithms on the Internet, I find it similar to my source code but they run correctly, so its really hard for me to find out the errors in my source code
#include <stdio.h>
#define MAX 2100
void downheap(int a[], int n, int k)
{
int i=0;
int temp = a[0];
while (k <= n/2)
{
i = k + k;
if(i<n && a[i] <= a[i+1]) i++;
if(temp < a[i]) break;
a[k] = a[i]; k = i;
}
a[k] = temp;
}
void heapsort(int a[], int n)
{
int i, j;
for(i=0; i<=n; i++) downheap(a, n, i);
while(n>=0)
{
j = a[0]; a[0] = a[n]; a[n] = j;
downheap(a, --n, 0);
}
}
int main()
{
int n, a[MAX], i;
printf("Enter your number of elements: ");
scanf("%d", &n);
for(i=0; i<n; i++) printf("%d: ", i), scanf("%d", &a[i]);
heapsort(a, n-1);
for(i=0; i<n; i++) printf("%d ", a[i]);
return 0;
}
There are multiple problems in your code that I list below:
You should stick to the common convention that n is the number of elements. In your code it is the number of elements minus one which is inconvenient. In this case you would call heapsort(a, n).
In the heapsort function, for(i=0; i<=n; i++) downheap(a, n, i) should be for(i=n/2-1; i>=0; i--) downheap(a, n, i).
Next, since n is the number of elements in a, the loop should be while(--n > 0). The iteration where n=0 is pointless since it will then swap a[0] with a[0]. Finally you call downheap(a, n, 0).
The function downheap, is where you have the biggest problem. The function should compare the element at index i with its two children and store the max of the tree at index i. If a swap with a child occurred, resume donwheap with this child. Your function is completely wrong. Here is a correct implementation.
void downheap(int *a, int n, int k){
int l = 2*k+1, r = 2*k+2, max = k;
if (l < n && a[l] > a[max])
max = l;
if (r < n &d a[r] > a[max])
max = r;
if (max != k) {
int j = a[k]; a[k] = a[max]; a[max] = j;
downheap(a, n, max);
}
}
As you can see, this code is very different from yours which is completely wrong.
For your convenience, here is the code of the heapsort function. That code was not as bad, but still incorrect.
void heapsort(int *a, int n){
int i, j;
for(i=n/2-1; i>=0; i--)
downheap(a, n, i);
while(--n > 0){
j = a[0]; a[0] = a[n]; a[n] = j;
downheap(a, n, 0);
}
}
EDIT
Non recursive implementation of downheap:
void downheap(int *a, int n, int k){
while (1) {
int l = 2*k+1, r = 2*k+2, max = k;
if (l < n && a[l] > a[max])
max = l;
if (r < n &d a[r] > a[max])
max = r;
if (max == k)
break;
int j = a[k]; a[k] = a[max]; a[max] = j;
k = max;
}
}

mergesort using C , the output goes wrong

I wrote the code in CodeBlocks 17.0.1
It seems that it only sorts some elements in the array, not all. Also, I can't find the differences between my code and the sample that I have and still don't know what goes wrong. Thank you for your help!
#include <stdio.h>
#include <stdlib.h>
void merge(float x[], float y[], int l, int m, int r)
{
int ptr_1,ptr_2,ptr_y;
ptr_1 = ptr_y = l;
ptr_2 = m+1;
while((ptr_1<=m) && (ptr_2<=r))
{
if(x[ptr_1] <= x[ptr_2])
y[ptr_y++] = x[ptr_1++];
else
{
y[ptr_y++] = x[ptr_2++];
}
}
while(ptr_1<=m)
y[ptr_y++] = x[ptr_1++];
while(ptr_2<=r)
y[ptr_y++] = x[ptr_2++];
}
void merge_sort(float a[],float b[],int l, int r)
{
if(l < r)
{ int m = l + (r-l)/2;
merge_sort(a,b,l,m);
merge_sort(a,b,m+1,r);
merge(a,b,l,m,r);
}
}
int main()
{
float a[3] ={10.3,8.5,3.23},b[3];
int i,j;
float *temp = b;
merge_sort(a,temp,0,2);
for(i=0;i<3;i++)
{
printf("%.2f ",b[i]);
}
printf("\n");
system("pause");
return 0;
}
Output: 3.23 10.30 8.50
Here is the working code:
#include <stdio.h>
#include <stdlib.h>
void merge(float a[], int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
/* create temp arrays */
float L[n1], R[n2];
/* Copy data to temp arrays L[] and R[] */
for (i = 0; i < n1; i++)
L[i] = a[l + i];
for (j = 0; j < n2; j++)
R[j] = a[m + 1+ j];
/* Merge the temp arrays back into arr[l..r]*/
i = 0; // Initial index of first subarray
j = 0; // Initial index of second subarray
k = l; // Initial index of merged subarray
while (i < n1 && j < n2)
{
if (L[i] <= R[j])
{
a[k] = L[i];
i++;
}
else
{
a[k] = R[j];
j++;
}
k++;
}
/* Copy the remaining elements of L[], if there
are any */
while (i < n1)
{
a[k] = L[i];
i++;
k++;
}
/* Copy the remaining elements of R[], if there
are any */
while (j < n2)
{
a[k] = R[j];
j++;
k++;
}
}
void merge_sort(float a[],int l, int r)
{
if(l < r)
{ int m = l + (r-l)/2;
merge_sort(a,l,m);
merge_sort(a,m+1,r);
merge(a,l,m,r);
}
}
int main()
{
float a[3] ={10.3,8.5,3.23};
int i,j;
merge_sort(a,0,2);
for(i=0;i<3;i++)
{
printf("%.2f ",a[i]);
}
printf("\n");
system("pause");
return 0;
}
still don't know what goes wrong
Why were you using two different arrays?
Just one array is enough but it includes two logical arrays and the indexes l and m refer to the beginning of each array.
Add the code shown below to the end of merge to copy the data back. There are more efficient ways to do this that avoid the copy back step, but at least this fixes the code.
void merge(float x[], float y[], int l, int m, int r)
{
/* after the last line in merge, (y[ptr_y++] = x[ptr_2++];) */
/* add this code to copy from y[] back to x[] */
for(ptr_1 = l; ptr_1 <= r; ptr1++)
x[ptr_1] = y[ptr_1];
}

Resources