Writing merge sort in C without pointers - c

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];

Related

Implementation of the quick select algorithm to find kth smallest number

I'm currently working on a program to find the kth smallest number of an array using the quick select algorithm. I've finished it and it works but does not give the correct result every time.
Here's my code (I didn't include my partition or swap algorithm, I'm fairly sure they're correct):
/*
inputs...
*A: pointer to array
n: size of array
k: the item in question
*/
int ksmallest(int *A, int n, int k){
int left = 0;
int right = n - 1;
int next = 1;
return quickselect(A, left, right, k);
}
int quickselect(int *A, int left, int right, int k){
//p is position of pivot in the partitioned array
int p = partition(A, left, right);
//k equals pivot got lucky
if (p - 1 == k - 1){
return A[p];
}
//k less than pivot
else if (k - 1 < p - 1){
return quickselect(A, left, p - 1, k);
}
//k greater than pivot
else{
return quickselect(A, p + 1, right, k);
}
}
Everything compiles fine. I then tried to use the program on the following array: [1,3,8,2,4,9,7]
These were my results:
> kthsm 2
4
> kthsm 1
1
> kthsm 3
2
As you can see, it worked correctly on the 1th smallest item, but failed on the others. What could be the problem? I guessed than my indexing was off but I'm not exactly sure.
EDIT: Added my partition and swap code below, as requested:
int partition(int *A, int left, int right){
int pivot = A[right], i = left, x;
for (x = left; x < right - 1; x++){
if (A[x] <= pivot){
swap(&A[i], &A[x]);
i++;
}
}
swap(&A[i], &A[right]);
return i;
}
//Swaps
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
In your partition function the loop condition should be x < right, not x < right - 1.
Also, in the if statements in quickselect, you should switch both uses of p-1 to p. p is already an index and by decreasing k by 1 you turn it into an index(rather than an order) as well. There is no need to decrease p by one again.
int partition(int *A, int left, int right){
int pivot = A[right], i = left, x;
for (x = left; x < right; x++){
if (A[x] < pivot){
swap(&A[i], &A[x]);
i++;
}
}
swap(&A[i], &A[right]);
return i;
}
int quickselect(int *A, int left, int right, int k){
//p is position of pivot in the partitioned array
int p = partition(A, left, right);
//k equals pivot got lucky
if (p == k-1){
return A[p];
}
//k less than pivot
else if (k - 1 < p){
return quickselect(A, left, p - 1, k);
}
//k greater than pivot
else{
return quickselect(A, p + 1, right, k);
}
}
Here's a working example. http://ideone.com/Bkaglb

Error in merge sort program

I am doing a merge sort program in C but I'm getting some unexpected output.
Can anyone find out error in the program?
#include<stdio.h>
int array[100],n;
void merge(int l,int m,int h);
void mergesort(int start,int end);
main(){
int i;
printf("Enter Size: ");
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",&array[i]);
}
mergesort(0, n-1);
for(i=0;i<n;i++){
printf("%d\n",array[i]);
}
}
void mergesort(int start,int end){
int mid;
if(start<end){
mid=(start+end)/2;
mergesort(start,mid);
mergesort(mid+1,end);
merge(start,mid,end);
}
}
void merge(int start,int mid,int end){
int i,j,k;
int a1[100],a2[100];
for(k=0,i=start;i<=mid;i++,k++){
a1[k]=array[i];
}
for(k=0,j=i;j<=end;j++,k++){
a2[k]=array[j];
}
a1[mid]=999;
a2[j]=999;
i=0;j=0;
for(k=start; k <=end; k++)
{
if(a1[i]<=a2[j])
array[k]=a1[i++];
else
array[k]=a2[j++];
}
}
Output:
Enter Size: 5
1 2 3 2 3
2
2
3
3
-1818025592
For larger arrays, the output is more scrambled. I think there is an error in merge() function.
You are terminating your a1 and a2 arrays at the wrong location.
Try changing:
for(k=0,i=start;i<=mid;i++,k++){
a1[k]=array[i];
}
for(k=0,j=i;j<=end;j++,k++){
a2[k]=array[j];
}
a1[mid]=999;
a2[j]=999;
to
for(k=0,i=start;i<=mid;i++,k++){
a1[k]=array[i];
}
a1[k]=999;
for(k=0,j=i;j<=end;j++,k++){
a2[k]=array[j];
}
a2[k]=999;
When you create your temporary arrays:
for (k = 0, i = start; i <= mid; i++, k++) {
a1[k] = array[i];
}
a1[i] = 999;
you put a sentinel value with a high value at the end. But i is the index into the original array. You must use k, the index of the temporary array a1:
for (k = 0, i = start; i <= mid; i++, k++) {
a1[k] = array[i];
}
a1[k] = 999;
Even so, the double control structure with k and i is clumsy. And there's no need to guess a high number; <limits.h> has constants defined for the min and max values of most types:
k = 0;
for (i = start; i <= mid; i++) {
a1[k++] = array[i];
}
a1[k] = INT_MAX;
Here, you increment k when an item is appended. This works even when the assignment happens conditionally, i.e. not in all iterations.
Finally, I would recommend to use exclusive upper bounds. That's the natural way to express ranges in C. mid would then be both the exclusive upper bound of the left array and the inclusive lower bound of the right array.
I have solved it
a[mid + 1]=999
a[k]=999

Segmentation error but I don't know why

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_SIZE 100
I am trying to quicksort an array of 2D points based upon their distance from the origin but my code hits a Seg fault after the first scanf.
typedef struct point{
double x;
double y;
double dist;
} point;
void sortpoints(point arr[], int low, int high);
void printpoints(point arr[], int n);
Sortpoints is just a manipulation of a quicksort in ordering an array of Point structs depending on the value of Point.dist
void sortpoints(point arr[], int low, int high){
int piv, i, j;
piv = low;
i = low;
j = high;
point box;
if(low < high){
while(i<j){
while((arr[i].dist)<=(arr[piv].dist) && i<= high)
i++;
}
while((arr[j].dist) > (arr[piv].dist) && j>= low)
j--;
}
if(i<j){
box = arr[i];
arr[i] = arr[j];
arr[j] = box;
}
box = arr[j];
arr[j] = arr[piv];
arr[piv] = box;
sortpoints(arr, low, j-1);
sortpoints(arr, j+1, high);
}
Printpoints just prints the points in order of their distances from the origin
void printpoints(point arr[], int n){
int i; for(i = 0; i <= n; i++){
printf("(%.2lf, %.2lf)\n", arr[i].x, arr[i].y);
}
}
The user enters the number of points and value of the points in the form (point.x, point.y)
int main(){
point pointa;
point pointarray[MAX_SIZE];
int n=0;
printf("how many points would you like to enter?\n");
scanf("%d", &n);
if(n<MAX_SIZE){
int i;
for(i=0; i<n ; i++){
scanf("(%lf,%lf)", &pointa.x, &pointa.y);
pointa.dist = sqrt(pointa.x*pointa.x + pointa.y*pointa.y);
pointarray[i] = pointa;
}
sortpoints(pointarray, 0, n-1);
printpoints(pointarray, n);}
else{
printf("sorry, not a valid array size\n");
}
return 0;
}
I've got stack overflow.
Your program calls function sortpoints, and calls itself too (sortpoints calls sortpoints). But i cant see where sortpoints must stop!
As a result, j-1 goes under null, and arr[j] is not valid at all.
When I changed this line:
scanf("(%lf,%lf)", &pointa.x, &pointa.y);
To this:
scanf("%lf,%lf", &pointa.x, &pointa.y);
The point values were actually being read in, with the parens there nothing gets read in, I don't see anything in the documentation on scanf about this though, is anyone familiar with this?
You sorting code seems to go into infinite recursion and cause a stack overflow however.
Your issue is with your braces. I have reformatted your sortpoints function slightly so it is clearer where each basic block actually is.
void sortpoints(point arr[], int low, int high){
int piv, i, j;
piv = low;
i = low;
j = high;
point box;
if(low < high){
while(i<j){
while((arr[i].dist)<=(arr[piv].dist) && i<= high)
i++;
}
while((arr[j].dist) > (arr[piv].dist) && j>= low)
j--;
}
if(i<j){
box = arr[i];
arr[i] = arr[j];
arr[j] = box;
}
box = arr[j];
arr[j] = arr[piv];
arr[piv] = box;
sortpoints(arr, low, j-1);
sortpoints(arr, j+1, high);
}
It should be evident now that your segfault is the result of accidental infinite recursion. This is a good argument for using { } for your loops, even with a single statement.

Segmentation fault while passing array as parameter in c

As an assignment I have to implement mergesort. I am getting segmentation faults as I am passing array as an argument. Everything appears to be correct. I am attaching the code.
arr is int arr[1000], I am passing it to mergesort as
mergesort(arr, 0, n);
Remaining code is as follows.
void merge(int a[], int l, int m, int r)
{
int temp[r -l];
int templ = l;
int tempm = m;
register int k;
for(k=0; k<(r-l); k++)
{
if((tempm >= r)||(templ < m)&&(a[templ] <= a[tempm]))
{ /*if number from left subarray is smaller*/
temp[k] = a[templ];
templ++;
}
else
{ /*number from right subarray is smaller*/
temp[k] = a[tempm];
tempm++;
}
}
for(k= l; k< r; k++)
{ /*copy results back to the array to be returned*/
a[k] = temp[k - l];
}
}
void mergesort(int ar[], int left, int right)
{
int mid;
if(left < right)
{
mid= (left + right)/2;
mergesort(&ar[0], left, mid - 1);
mergesort(&ar[0], mid, right);
merge(&ar[0], left, mid, right);
}
}
Once you have left == 0 and right == 1 in mergesort, you are going to have an infinite recursion:
void mergesort(int ar[], int left, int right) // IF LEFT IS 0 AND RIGHT IS 1 HERE...
{
int mid;
if(left < right) // ...THEN LEFT IS LESS THAN RIGHT AND...
{
mid= (left + right)/2;
mergesort(&ar[0], left, mid - 1);
mergesort(&ar[0], mid, right); // ...HERE WE HAVE A CALL THAT IS
// IDENTICAL TO THE ONE THAT GOT US HERE, INFINITE RECURSION

QuickSort doesn't work for large inputs

Can anyone spot a problem with my quick sort implementation below? It seems to fail on arrays with more than 10 or so elements.
void swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void generateRandom(int arr[], int size)
{
srand(time(NULL));
int i;
for (i = 0; i < size; i++)
{
arr[i] = rand() % 100;
}
}
int partition(int arr[], int start, int end)
{
int i = start, j = end;
int pivot = arr[start];
for (;;)
{
for (; arr[i] < pivot; i++);
for (; arr[j] > pivot; j--);
if (i < j)
{
swap(&arr[i], &arr[j]);
}
else
{
return j;
}
}
}
void quickSort(int arr[], int start, int end)
{
int part;
if (start < end)
{
part = partition(arr, start, end);
quickSort(arr, start, part);
quickSort(arr, part + 1, end);
}
}
int main()
{
generateRandom(arr, 100);
for (i = 0; i < 100; i++)
{
printf("%d ", arr[i]);
}
printf("\n\n");
quickSort(arr, 0, 99);
for (i = 0; i < 100; i++)
{
printf("%d ", arr[i]);
}
printf("\n\n");
return 0;
}
First, you code doesn't compile. When I made the corrections to make it compile (adding stdio.h, and definitions for arr and i in main) it infinite looped, which it will do if the partition starts and ends with the pivot. You need to increment and decrement before the comparisons rather than after. You can do that by starting with i = start-1 and j = end+1 and changing your inner loops to increment or decrement first, or you can leave them as is and just do an i++ and j-- after the swap -- I did that and the sort works.
Note that your pivot choice is poor for already sorted arrays; you really should be picking the median of 3 or even 9 values.
P.S. Other desirable optimizations: 1) Switch to an insertion sort for small partitions -- the optimal cutoff point is machine-dependent. Another approach is to not sort partitions below a certain size, and then do an insertion sort on the whole array after quicksort is done. It's also possible to use heapsort instead of insertion sort if done carefully; google introsort. 2) quicksort does two recursive calls; eliminate the second one by setting start = part + 1 and looping. 3) Avoid the possibility of stack overflow by quicksorting the larger partition first. 4) Eliminate the first recursive call by using an explicit stack. 5) Inline swap() and partition(). 6) Don't bother with any of that and just call the qsort library routine. :-)
I had the same problem,
but I changed my while loop to do..while and it worked.
This is my new code now.
int partition(int a[], int lo, int hi) {
int v = a[lo], i = lo, j = hi;
do {
do {
i++;
} while(a[i] < v) ;
do {
j--;
}while(a[j] > v) ;
if(i < j) interchange(&a[i], &a[j]);
}while(i < j);
interchange(&a[lo], &a[j]);
return j;
}

Resources